aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYotam Barnoy2010-10-31 11:08:43 +0000
committerYotam Barnoy2010-10-31 11:08:43 +0000
commit94c8d0a14df429a1b25bd9f5c5d75497fd0ddbd1 (patch)
tree3df2a4ae7967c56d464729669fc06ce4e93dff36
parent8df4278ba8cfbf71228e1927f9db635a9a30a57f (diff)
parentdca3c8d8bfc6c4db38cf8e8291818dd472041d4e (diff)
downloadscummvm-rg350-94c8d0a14df429a1b25bd9f5c5d75497fd0ddbd1.tar.gz
scummvm-rg350-94c8d0a14df429a1b25bd9f5c5d75497fd0ddbd1.tar.bz2
scummvm-rg350-94c8d0a14df429a1b25bd9f5c5d75497fd0ddbd1.zip
Updated with latest from trunk
svn-id: r53976
-rw-r--r--AUTHORS31
-rw-r--r--Makefile10
-rw-r--r--Makefile.common5
-rw-r--r--NEWS96
-rw-r--r--README50
-rw-r--r--backends/events/default/default-events.cpp1
-rw-r--r--backends/fs/psp/psp-fs.cpp12
-rw-r--r--backends/fs/psp/psp-stream.cpp66
-rw-r--r--backends/fs/psp/psp-stream.h24
-rw-r--r--backends/fs/stdiostream.cpp3
-rw-r--r--backends/fs/windows/windows-fs-factory.cpp4
-rw-r--r--backends/midi/alsa.cpp3
-rw-r--r--backends/midi/camd.cpp3
-rw-r--r--backends/midi/coreaudio.cpp3
-rw-r--r--backends/midi/coremidi.cpp3
-rw-r--r--backends/midi/dmedia.cpp3
-rw-r--r--backends/midi/seq.cpp3
-rw-r--r--backends/midi/stmidi.cpp3
-rw-r--r--backends/midi/timidity.cpp7
-rw-r--r--backends/midi/windows.cpp5
-rw-r--r--backends/platform/android/README.build3
-rw-r--r--backends/platform/android/android.cpp22
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVM.java123
-rw-r--r--backends/platform/android/video.cpp40
-rw-r--r--backends/platform/dc/selector.cpp2
-rw-r--r--backends/platform/dingux/README.DINGUX68
-rw-r--r--backends/platform/dingux/dingux-events.cpp2
-rw-r--r--backends/platform/dingux/dingux.mk30
-rw-r--r--backends/platform/dingux/scummvm.gpe5
-rw-r--r--backends/platform/ds/arm9/makefile2
-rw-r--r--backends/platform/ds/arm9/source/touchkeyboard.cpp8
-rw-r--r--backends/platform/gp2x/build/README-GP2X150
-rwxr-xr-xbackends/platform/gp2x/build/config.sh3
-rwxr-xr-xbackends/platform/gp2x/build/scummvm.gpe2
-rw-r--r--backends/platform/gp2x/events.cpp855
-rwxr-xr-xbackends/platform/gp2x/gp2x-bundle.mk12
-rw-r--r--backends/platform/gp2x/gp2x-common.h25
-rw-r--r--backends/platform/gp2x/gp2x-hw.cpp28
-rw-r--r--backends/platform/gp2x/gp2x-hw.h8
-rw-r--r--backends/platform/gp2x/gp2x-mem.cpp3
-rw-r--r--backends/platform/gp2x/gp2x.cpp3
-rw-r--r--backends/platform/gp2x/graphics.cpp30
-rwxr-xr-xbackends/platform/gp2xwiz/caanoo/config-alleng.sh16
-rwxr-xr-xbackends/platform/gp2xwiz/caanoo/config.sh16
-rw-r--r--backends/platform/gp2xwiz/gp2xwiz-events.cpp501
-rw-r--r--backends/platform/gph/build/README-GPH (renamed from backends/platform/gp2xwiz/build/README-GP2XWIZ)8
-rwxr-xr-xbackends/platform/gph/build/build.sh (renamed from backends/platform/gp2xwiz/build/build.sh)0
-rwxr-xr-xbackends/platform/gph/build/bundle-debug.sh (renamed from backends/platform/gp2xwiz/build/bundle-debug.sh)0
-rwxr-xr-xbackends/platform/gph/build/bundle.sh (renamed from backends/platform/gp2xwiz/build/bundle.sh)0
-rwxr-xr-xbackends/platform/gph/build/clean.sh (renamed from backends/platform/gp2xwiz/build/clean.sh)0
-rwxr-xr-xbackends/platform/gph/build/config-alleng.sh (renamed from backends/platform/gp2xwiz/build/config-alleng.sh)9
-rwxr-xr-xbackends/platform/gph/build/config.sh (renamed from backends/platform/gp2xwiz/build/config.sh)9
-rwxr-xr-xbackends/platform/gph/build/scummvm-gdb.gpe (renamed from backends/platform/gp2xwiz/build/scummvm-gdb.gpe)2
-rwxr-xr-xbackends/platform/gph/build/scummvm.gpe (renamed from backends/platform/gp2xwiz/build/scummvm.gpe)2
-rw-r--r--backends/platform/gph/build/scummvm.ini (renamed from backends/platform/gp2xwiz/build/scummvm.ini)1
-rw-r--r--backends/platform/gph/build/scummvm.png (renamed from backends/platform/gp2xwiz/build/scummvm.png)bin2656 -> 2656 bytes
-rw-r--r--backends/platform/gph/build/scummvmb.pngbin0 -> 34274 bytes
-rwxr-xr-xbackends/platform/gph/caanoo-bundle.mk (renamed from backends/platform/gp2xwiz/caanoo/caanoo-bundle.mk)28
-rwxr-xr-xbackends/platform/gph/caanoo/build.sh (renamed from backends/platform/gp2xwiz/caanoo/build.sh)0
-rwxr-xr-xbackends/platform/gph/caanoo/bundle-debug.sh (renamed from backends/platform/gp2xwiz/caanoo/bundle-debug.sh)0
-rwxr-xr-xbackends/platform/gph/caanoo/bundle.sh (renamed from backends/platform/gp2xwiz/caanoo/bundle.sh)0
-rwxr-xr-xbackends/platform/gph/caanoo/clean.sh (renamed from backends/platform/gp2xwiz/caanoo/clean.sh)0
-rwxr-xr-xbackends/platform/gph/caanoo/config-alleng.sh21
-rwxr-xr-xbackends/platform/gph/caanoo/config.sh21
-rwxr-xr-xbackends/platform/gph/caanoo/scummvm-gdb.gpe (renamed from backends/platform/gp2xwiz/caanoo/scummvm-gdb.gpe)2
-rwxr-xr-xbackends/platform/gph/caanoo/scummvm.gpe (renamed from backends/platform/gp2xwiz/caanoo/scummvm.gpe)2
-rwxr-xr-xbackends/platform/gph/gp2xwiz-bundle.mk (renamed from backends/platform/gp2xwiz/gp2xwiz-bundle.mk)30
-rw-r--r--backends/platform/gph/gph-events.cpp481
-rw-r--r--backends/platform/gph/gph-graphics.cpp (renamed from backends/platform/gp2xwiz/gp2xwiz-graphics.cpp)30
-rw-r--r--backends/platform/gph/gph-hw.cpp (renamed from backends/platform/gp2xwiz/gp2xwiz-hw.cpp)30
-rw-r--r--backends/platform/gph/gph-hw.h (renamed from backends/platform/gp2xwiz/gp2xwiz-hw.h)14
-rw-r--r--backends/platform/gph/gph-main.cpp (renamed from backends/platform/gp2xwiz/gp2xwiz-main.cpp)19
-rw-r--r--backends/platform/gph/gph-sdl.h (renamed from backends/platform/gp2xwiz/gp2xwiz-sdl.h)16
-rw-r--r--backends/platform/gph/module.mk (renamed from backends/platform/gp2xwiz/module.mk)10
-rw-r--r--backends/platform/iphone/iphone_common.h9
-rw-r--r--backends/platform/iphone/iphone_main.m13
-rw-r--r--backends/platform/iphone/iphone_video.h7
-rw-r--r--backends/platform/iphone/iphone_video.m292
-rw-r--r--backends/platform/iphone/osys_events.cpp41
-rw-r--r--backends/platform/iphone/osys_main.cpp9
-rw-r--r--backends/platform/iphone/osys_main.h5
-rw-r--r--backends/platform/iphone/osys_sound.cpp3
-rw-r--r--backends/platform/iphone/osys_video.cpp102
-rw-r--r--backends/platform/linuxmoto/linuxmoto-events.cpp2
-rw-r--r--backends/platform/linuxmoto/linuxmoto-main.cpp3
-rw-r--r--backends/platform/n64/Makefile8
-rw-r--r--backends/platform/n64/README.N646
-rw-r--r--backends/platform/n64/module.mk2
-rw-r--r--backends/platform/n64/n64.mk29
-rw-r--r--backends/platform/n64/osys_n64_base.cpp4
-rw-r--r--backends/platform/n64/pad_rom.sh6
-rwxr-xr-xbackends/platform/openpandora/build/PXML.xml34
-rwxr-xr-xbackends/platform/openpandora/build/README-OPENPANDORA19
-rwxr-xr-xbackends/platform/openpandora/build/README-PND.txt38
-rwxr-xr-xbackends/platform/openpandora/build/build.sh20
-rwxr-xr-xbackends/platform/openpandora/build/bundle.sh13
-rwxr-xr-xbackends/platform/openpandora/build/clean.sh10
-rwxr-xr-xbackends/platform/openpandora/build/config-alleng.sh29
-rwxr-xr-xbackends/platform/openpandora/build/config.sh29
-rwxr-xr-xbackends/platform/openpandora/build/icon/preview-pic.pngbin0 -> 72496 bytes
-rwxr-xr-xbackends/platform/openpandora/build/icon/scummvm.pngbin0 -> 2656 bytes
-rwxr-xr-xbackends/platform/openpandora/build/index.html26
-rwxr-xr-xbackends/platform/openpandora/build/pnd_make.sh65
-rwxr-xr-xbackends/platform/openpandora/build/runscummvm.sh14
-rwxr-xr-xbackends/platform/openpandora/module.mk16
-rwxr-xr-xbackends/platform/openpandora/op-bundle.mk85
-rwxr-xr-xbackends/platform/openpandora/op-events.cpp176
-rwxr-xr-xbackends/platform/openpandora/op-graphics.cpp50
-rwxr-xr-xbackends/platform/openpandora/op-main.cpp265
-rwxr-xr-xbackends/platform/openpandora/op-options.cpp56
-rwxr-xr-x[-rw-r--r--]backends/platform/openpandora/op-options.h (renamed from engines/gob/helper.h)24
-rwxr-xr-xbackends/platform/openpandora/op-sdl.h59
-rw-r--r--backends/platform/ps2/Makefile.gdb8
-rw-r--r--backends/platform/ps2/Makefile.ps2102
-rw-r--r--backends/platform/ps2/fileio.cpp3
-rw-r--r--backends/platform/ps2/iop/CoDyVDfs/iop/imports.lst10
-rw-r--r--backends/platform/ps2/systemps2.cpp3
-rw-r--r--backends/platform/psp/Makefile9
-rw-r--r--backends/platform/psp/README.PSP169
-rw-r--r--backends/platform/psp/README.PSP.in132
-rw-r--r--backends/platform/psp/audio.cpp38
-rw-r--r--backends/platform/psp/audio.h8
-rw-r--r--backends/platform/psp/cursor.h2
-rw-r--r--backends/platform/psp/display_client.cpp185
-rw-r--r--backends/platform/psp/display_client.h23
-rw-r--r--backends/platform/psp/display_manager.cpp129
-rw-r--r--backends/platform/psp/display_manager.h55
-rw-r--r--backends/platform/psp/dummy.cpp59
-rw-r--r--backends/platform/psp/image_viewer.cpp327
-rw-r--r--backends/platform/psp/image_viewer.h105
-rw-r--r--backends/platform/psp/input.cpp623
-rw-r--r--backends/platform/psp/input.h169
-rw-r--r--backends/platform/psp/memory.cpp405
-rw-r--r--backends/platform/psp/memory.h122
-rw-r--r--backends/platform/psp/module.mk5
-rw-r--r--backends/platform/psp/mp3.cpp90
-rw-r--r--backends/platform/psp/mp3.h23
-rw-r--r--backends/platform/psp/osys_psp.cpp52
-rw-r--r--backends/platform/psp/osys_psp.h4
-rw-r--r--backends/platform/psp/png_loader.cpp210
-rw-r--r--backends/platform/psp/png_loader.h74
-rw-r--r--backends/platform/psp/powerman.cpp52
-rw-r--r--backends/platform/psp/psp.spec2
-rw-r--r--backends/platform/psp/psp_main.cpp6
-rw-r--r--backends/platform/psp/pspkeyboard.cpp208
-rw-r--r--backends/platform/psp/pspkeyboard.h11
-rw-r--r--backends/platform/psp/rtc.cpp30
-rw-r--r--backends/platform/psp/rtc.h4
-rw-r--r--backends/platform/psp/tests.cpp248
-rw-r--r--backends/platform/psp/tests.h6
-rw-r--r--backends/platform/psp/thread.cpp60
-rw-r--r--backends/platform/psp/thread.h6
-rw-r--r--backends/platform/sdl/graphics.cpp10
-rw-r--r--backends/platform/sdl/main.cpp4
-rw-r--r--backends/platform/sdl/sdl.cpp44
-rw-r--r--backends/platform/sdl/sdl.h2
-rw-r--r--backends/platform/symbian/AdaptAllMMPs.pl138
-rw-r--r--backends/platform/symbian/BuildPackageUpload_AllVersions.pl88
-rw-r--r--backends/platform/symbian/BuildPackageUpload_LocalSettings.pl58
-rw-r--r--backends/platform/symbian/README4
-rw-r--r--backends/platform/symbian/S60/BLD.INF.in2
-rw-r--r--backends/platform/symbian/S60v3/BLD.INF.in2
-rw-r--r--backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in7
-rw-r--r--backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in7
-rw-r--r--backends/platform/symbian/S60v3/scummvm-CVS-SymbianS60v3.pkg3
-rw-r--r--backends/platform/symbian/S80/BLD.INF.in2
-rw-r--r--backends/platform/symbian/S90/BLD.INF.in2
-rw-r--r--backends/platform/symbian/UIQ2/BLD.INF.in2
-rw-r--r--backends/platform/symbian/UIQ3/BLD.INF.in4
-rw-r--r--backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in2
-rw-r--r--backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in2
-rw-r--r--backends/platform/symbian/UIQ3/scummvm-CVS-SymbianUIQ3.pkg4
-rw-r--r--backends/platform/symbian/mmp/scummvm_base.mmp.in3
-rw-r--r--backends/platform/symbian/mmp/scummvm_toon.mmp.in64
-rw-r--r--backends/platform/symbian/res/scummvm.rss1
-rw-r--r--backends/platform/symbian/src/ScummApp.cpp2
-rw-r--r--backends/platform/symbian/src/ScummApp.h4
-rw-r--r--backends/platform/symbian/src/ScummVMApp.h4
-rw-r--r--backends/platform/symbian/src/SymbianOS.cpp10
-rw-r--r--backends/platform/symbian/src/SymbianOS.h6
-rw-r--r--backends/platform/wii/options.cpp2
-rw-r--r--backends/platform/wince/CEActionsPocket.h4
-rw-r--r--backends/platform/wince/CEActionsSmartphone.h4
-rw-r--r--backends/platform/wince/CEDevice.h4
-rw-r--r--backends/platform/wince/CELauncherDialog.h4
-rw-r--r--backends/platform/wince/CEScaler.h4
-rw-r--r--backends/platform/wince/CEgui/GUIElement.h4
-rw-r--r--backends/platform/wince/CEgui/ItemAction.h4
-rw-r--r--backends/platform/wince/CEgui/ItemSwitch.h4
-rw-r--r--backends/platform/wince/CEgui/Panel.h4
-rw-r--r--backends/platform/wince/CEgui/PanelItem.h4
-rw-r--r--backends/platform/wince/CEgui/PanelKeyboard.h4
-rw-r--r--backends/platform/wince/CEgui/SDL_ImageResource.h4
-rw-r--r--backends/platform/wince/CEgui/Toolbar.h4
-rw-r--r--backends/platform/wince/CEgui/ToolbarHandler.h4
-rw-r--r--backends/platform/wince/CEkeys/EventsBuffer.h4
-rw-r--r--backends/platform/wince/wince-sdl.cpp16
-rw-r--r--backends/plugins/elf/elf-loader.cpp2
-rw-r--r--backends/plugins/elf/mips-loader.cpp2
-rw-r--r--backends/plugins/sdl/sdl-provider.cpp3
-rw-r--r--backends/timer/psp/timer.cpp16
-rw-r--r--backends/timer/psp/timer.h6
-rwxr-xr-xbackends/vkeybd/packs/vkeybdpack.py14
-rw-r--r--backends/vkeybd/virtual-keyboard-parser.cpp6
-rw-r--r--base/commandLine.cpp33
-rw-r--r--base/internal_version.h4
-rw-r--r--base/internal_version.h.in2
-rw-r--r--base/main.cpp8
-rw-r--r--base/plugins.cpp15
-rw-r--r--base/version.cpp8
-rw-r--r--common/algorithm.h8
-rw-r--r--common/array.h6
-rw-r--r--common/forbidden.h147
-rw-r--r--common/hashmap.h5
-rw-r--r--common/macresman.cpp24
-rw-r--r--common/macresman.h11
-rw-r--r--common/memorypool.h2
-rw-r--r--common/mutex.h4
-rw-r--r--common/rational.cpp29
-rw-r--r--common/rational.h5
-rw-r--r--common/scummsys.h7
-rw-r--r--common/str.cpp31
-rw-r--r--common/stream.cpp32
-rw-r--r--common/stream.h6
-rw-r--r--common/translation.cpp31
-rw-r--r--common/translation.h11
-rw-r--r--common/unzip.cpp4
-rw-r--r--common/util.h14
-rw-r--r--common/xmlparser.cpp97
-rw-r--r--common/xmlparser.h100
-rwxr-xr-xconfigure368
-rw-r--r--dists/android/AndroidManifest.xml6
-rwxr-xr-xdists/engine-data/create-testbed-data.sh52
-rw-r--r--dists/engine-data/drascula.datbin218023 -> 218799 bytes
-rw-r--r--dists/engine-data/hugo.datbin168169 -> 174491 bytes
-rw-r--r--dists/engine-data/kyra.datbin355807 -> 355898 bytes
-rw-r--r--dists/engine-data/testbed-audiocd-files/music.midbin0 -> 54376 bytes
-rw-r--r--dists/engine-data/testbed-audiocd-files/track01.mp3bin0 -> 15300 bytes
-rw-r--r--dists/engine-data/testbed-audiocd-files/track02.mp3bin0 -> 15927 bytes
-rw-r--r--dists/engine-data/testbed-audiocd-files/track03.mp3bin0 -> 11389 bytes
-rw-r--r--dists/engine-data/testbed-audiocd-files/track04.mp3bin0 -> 16136 bytes
-rw-r--r--dists/engine-data/toon.datbin0 -> 17986 bytes
-rw-r--r--dists/iphone/Info.plist9
-rw-r--r--dists/iphone/Info.plist.in14
-rw-r--r--dists/iphone/icon4.pngbin0 -> 15849 bytes
-rwxr-xr-xdists/iphone/scummvm.xcodeproj/project.pbxproj1793
-rw-r--r--dists/irix/scummvm.spec2
-rw-r--r--dists/macosx/Info.plist8
-rw-r--r--dists/macosx/Info.plist.in2
-rw-r--r--dists/redhat/scummvm-tools.spec7
-rw-r--r--dists/redhat/scummvm-tools.spec.in5
-rw-r--r--dists/redhat/scummvm.spec47
-rw-r--r--dists/redhat/scummvm.spec.in45
-rw-r--r--dists/redhat/scummvm48.pngbin0 -> 3124 bytes
-rw-r--r--dists/scummvm.rc8
-rwxr-xr-xdists/slackware/scummvm.SlackBuild2
-rw-r--r--dists/wii/meta.xml2
-rw-r--r--dists/wii/meta.xml.in1
-rw-r--r--engines/advancedDetector.cpp10
-rw-r--r--engines/agi/agi.cpp55
-rw-r--r--engines/agi/agi.h14
-rw-r--r--engines/agi/console.cpp2
-rw-r--r--engines/agi/cycle.cpp4
-rw-r--r--engines/agi/detection.cpp3
-rw-r--r--engines/agi/id.cpp4
-rw-r--r--engines/agi/inv.cpp2
-rw-r--r--engines/agi/keyboard.cpp1
-rw-r--r--engines/agi/loader_v2.cpp4
-rw-r--r--engines/agi/lzw.cpp2
-rw-r--r--engines/agi/objects.cpp15
-rw-r--r--engines/agi/op_cmd.cpp15
-rw-r--r--engines/agi/op_dbg.cpp20
-rw-r--r--engines/agi/picture.cpp2
-rw-r--r--engines/agi/preagi_mickey.cpp6
-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_coco3.cpp4
-rw-r--r--engines/agi/sound_midi.cpp19
-rw-r--r--engines/agi/sound_pcjr.cpp5
-rw-r--r--engines/agi/sound_sarien.cpp7
-rw-r--r--engines/agi/words.cpp4
-rw-r--r--engines/agos/agos.cpp12
-rw-r--r--engines/agos/agos.h9
-rw-r--r--engines/agos/animation.cpp4
-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/script_pn.cpp2
-rw-r--r--engines/agos/sound.cpp2
-rw-r--r--engines/agos/verb_pn.cpp2
-rw-r--r--engines/cine/cine.h2
-rw-r--r--engines/cine/pal.cpp3
-rw-r--r--engines/cine/part.cpp2
-rw-r--r--engines/cine/script_fw.cpp2
-rw-r--r--engines/cruise/background.cpp3
-rw-r--r--engines/cruise/cruise.h2
-rw-r--r--engines/cruise/dataLoader.cpp2
-rw-r--r--engines/cruise/function.cpp4
-rw-r--r--engines/cruise/menu.cpp5
-rw-r--r--engines/cruise/script.cpp3
-rw-r--r--engines/cruise/vars.cpp4
-rw-r--r--engines/cruise/vars.h4
-rw-r--r--engines/dialogs.cpp11
-rw-r--r--engines/draci/detection.cpp5
-rw-r--r--engines/draci/draci.cpp6
-rw-r--r--engines/draci/draci.h7
-rw-r--r--engines/draci/game.cpp14
-rw-r--r--engines/draci/mouse.cpp8
-rw-r--r--engines/draci/music.cpp16
-rw-r--r--engines/draci/saveload.cpp4
-rw-r--r--engines/draci/sound.cpp14
-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/drascula.h4
-rw-r--r--engines/drascula/interface.cpp11
-rw-r--r--engines/drascula/objects.cpp9
-rw-r--r--engines/drascula/resource.cpp10
-rw-r--r--engines/drascula/rooms.cpp4
-rw-r--r--engines/drascula/saveload.cpp8
-rw-r--r--engines/engine.cpp25
-rw-r--r--engines/engine.h29
-rw-r--r--engines/engines.mk20
-rw-r--r--engines/gob/console.cpp147
-rw-r--r--engines/gob/console.h56
-rw-r--r--engines/gob/dataio.cpp6
-rw-r--r--engines/gob/demos/demoplayer.cpp18
-rw-r--r--engines/gob/detection_tables.h238
-rw-r--r--engines/gob/draw.cpp37
-rw-r--r--engines/gob/draw.h22
-rw-r--r--engines/gob/draw_fascin.cpp233
-rw-r--r--engines/gob/draw_playtoons.cpp83
-rw-r--r--engines/gob/draw_v1.cpp65
-rw-r--r--engines/gob/draw_v2.cpp78
-rw-r--r--engines/gob/driver_vga.cpp244
-rw-r--r--engines/gob/driver_vga.h56
-rw-r--r--engines/gob/game.cpp25
-rw-r--r--engines/gob/global.h2
-rw-r--r--engines/gob/gob.cpp72
-rw-r--r--engines/gob/gob.h42
-rw-r--r--engines/gob/goblin.cpp146
-rw-r--r--engines/gob/goblin_v1.cpp52
-rw-r--r--engines/gob/goblin_v2.cpp302
-rw-r--r--engines/gob/goblin_v4.cpp126
-rw-r--r--engines/gob/hotspots.cpp39
-rw-r--r--engines/gob/init.cpp3
-rw-r--r--engines/gob/init_v3.cpp7
-rw-r--r--engines/gob/inter.cpp35
-rw-r--r--engines/gob/inter.h1
-rw-r--r--engines/gob/inter_bargon.cpp12
-rw-r--r--engines/gob/inter_fascin.cpp3
-rw-r--r--engines/gob/inter_playtoons.cpp8
-rw-r--r--engines/gob/inter_v1.cpp28
-rw-r--r--engines/gob/inter_v2.cpp32
-rw-r--r--engines/gob/inter_v4.cpp8
-rw-r--r--engines/gob/inter_v5.cpp4
-rw-r--r--engines/gob/inter_v6.cpp23
-rw-r--r--engines/gob/map.cpp451
-rw-r--r--engines/gob/map.h193
-rw-r--r--engines/gob/map_v1.cpp16
-rw-r--r--engines/gob/map_v2.cpp31
-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.cpp16
-rw-r--r--engines/gob/palanim.cpp2
-rw-r--r--engines/gob/save/savefile.cpp16
-rw-r--r--engines/gob/save/savefile.h45
-rw-r--r--engines/gob/save/savehandler.cpp8
-rw-r--r--engines/gob/save/savehandler.h4
-rw-r--r--engines/gob/scenery.cpp18
-rw-r--r--engines/gob/sound/adlib.cpp2
-rw-r--r--engines/gob/sound/cdrom.cpp4
-rw-r--r--engines/gob/surface.cpp584
-rw-r--r--engines/gob/surface.h131
-rw-r--r--engines/gob/util.cpp13
-rw-r--r--engines/gob/variables.cpp4
-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.cpp5
-rw-r--r--engines/gob/videoplayer.h2
-rw-r--r--engines/groovie/detection.cpp15
-rw-r--r--engines/groovie/font.cpp4
-rw-r--r--engines/groovie/groovie.cpp11
-rw-r--r--engines/groovie/groovie.h2
-rw-r--r--engines/groovie/music.cpp4
-rw-r--r--engines/hugo/detection.cpp13
-rw-r--r--engines/hugo/display.cpp227
-rw-r--r--engines/hugo/display.h57
-rw-r--r--engines/hugo/display_v1d.cpp83
-rw-r--r--engines/hugo/display_v1w.cpp83
-rw-r--r--engines/hugo/engine.cpp973
-rw-r--r--engines/hugo/file.cpp811
-rw-r--r--engines/hugo/file.h72
-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.h96
-rw-r--r--engines/hugo/global.h13
-rw-r--r--engines/hugo/hugo.cpp854
-rw-r--r--engines/hugo/hugo.h153
-rw-r--r--engines/hugo/intro.cpp142
-rw-r--r--engines/hugo/intro.h40
-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.cpp98
-rw-r--r--engines/hugo/inventory.h4
-rw-r--r--engines/hugo/module.mk26
-rw-r--r--engines/hugo/mouse.cpp163
-rw-r--r--engines/hugo/mouse.h4
-rw-r--r--engines/hugo/object.cpp429
-rw-r--r--engines/hugo/object.h131
-rw-r--r--engines/hugo/object_v1d.cpp358
-rw-r--r--engines/hugo/object_v1w.cpp370
-rw-r--r--engines/hugo/object_v2d.cpp352
-rw-r--r--engines/hugo/object_v3d.cpp253
-rw-r--r--engines/hugo/parser.cpp567
-rw-r--r--engines/hugo/parser.h86
-rw-r--r--engines/hugo/parser_v1d.cpp356
-rw-r--r--engines/hugo/parser_v1w.cpp435
-rw-r--r--engines/hugo/parser_v2d.cpp138
-rw-r--r--engines/hugo/parser_v3d.cpp204
-rw-r--r--engines/hugo/route.cpp224
-rw-r--r--engines/hugo/route.h4
-rw-r--r--engines/hugo/schedule.cpp603
-rw-r--r--engines/hugo/schedule.h97
-rw-r--r--engines/hugo/schedule_v1d.cpp406
-rw-r--r--engines/hugo/schedule_v1w.cpp481
-rw-r--r--engines/hugo/schedule_v2d.cpp385
-rw-r--r--engines/hugo/schedule_v3d.cpp326
-rw-r--r--engines/hugo/sound.cpp22
-rw-r--r--engines/hugo/sound.h6
-rw-r--r--engines/hugo/util.cpp91
-rw-r--r--engines/hugo/util.h7
-rw-r--r--engines/kyra/animator_v2.cpp2
-rw-r--r--engines/kyra/detection_tables.h33
-rw-r--r--engines/kyra/gui_hof.cpp58
-rw-r--r--engines/kyra/gui_lok.cpp17
-rw-r--r--engines/kyra/gui_lol.cpp2
-rw-r--r--engines/kyra/gui_mr.cpp24
-rw-r--r--engines/kyra/gui_v2.cpp2
-rw-r--r--engines/kyra/item.h (renamed from engines/hugo/engine.h)29
-rw-r--r--engines/kyra/items_hof.cpp34
-rw-r--r--engines/kyra/items_lok.cpp57
-rw-r--r--engines/kyra/items_lol.cpp28
-rw-r--r--engines/kyra/items_mr.cpp46
-rw-r--r--engines/kyra/items_v2.cpp22
-rw-r--r--engines/kyra/kyra_hof.cpp32
-rw-r--r--engines/kyra/kyra_hof.h26
-rw-r--r--engines/kyra/kyra_lok.cpp17
-rw-r--r--engines/kyra/kyra_lok.h24
-rw-r--r--engines/kyra/kyra_mr.cpp30
-rw-r--r--engines/kyra/kyra_mr.h20
-rw-r--r--engines/kyra/kyra_v1.cpp17
-rw-r--r--engines/kyra/kyra_v1.h9
-rw-r--r--engines/kyra/kyra_v2.h26
-rw-r--r--engines/kyra/lol.cpp4
-rw-r--r--engines/kyra/lol.h38
-rw-r--r--engines/kyra/resource_intern.cpp2
-rw-r--r--engines/kyra/saveload.cpp4
-rw-r--r--engines/kyra/saveload_hof.cpp10
-rw-r--r--engines/kyra/saveload_lok.cpp6
-rw-r--r--engines/kyra/saveload_lol.cpp10
-rw-r--r--engines/kyra/saveload_mr.cpp10
-rw-r--r--engines/kyra/scene_hof.cpp12
-rw-r--r--engines/kyra/scene_lok.cpp5
-rw-r--r--engines/kyra/scene_lol.cpp2
-rw-r--r--engines/kyra/scene_mr.cpp22
-rw-r--r--engines/kyra/screen.cpp10
-rw-r--r--engines/kyra/screen.h1
-rw-r--r--engines/kyra/screen_hof.h1
-rw-r--r--engines/kyra/screen_lok.h1
-rw-r--r--engines/kyra/screen_lol.cpp2
-rw-r--r--engines/kyra/screen_lol.h3
-rw-r--r--engines/kyra/screen_mr.h1
-rw-r--r--engines/kyra/screen_v2.cpp12
-rw-r--r--engines/kyra/script_hof.cpp8
-rw-r--r--engines/kyra/script_lok.cpp5
-rw-r--r--engines/kyra/script_lol.cpp7
-rw-r--r--engines/kyra/script_mr.cpp10
-rw-r--r--engines/kyra/script_tim.cpp4
-rw-r--r--engines/kyra/script_v2.cpp2
-rw-r--r--engines/kyra/sequences_hof.cpp4
-rw-r--r--engines/kyra/sequences_lol.cpp21
-rw-r--r--engines/kyra/sound.h2
-rw-r--r--engines/kyra/sound_adlib.cpp2
-rw-r--r--engines/kyra/sound_adlib.h2
-rw-r--r--engines/kyra/sound_digital.cpp5
-rw-r--r--engines/kyra/sound_intern.h2
-rw-r--r--engines/kyra/sound_midi.cpp14
-rw-r--r--engines/kyra/sound_towns.cpp15
-rw-r--r--engines/kyra/sprites.cpp4
-rw-r--r--engines/kyra/sprites_lol.cpp9
-rw-r--r--engines/kyra/staticres.cpp23
-rw-r--r--engines/kyra/text_hof.cpp2
-rw-r--r--engines/kyra/text_lol.cpp5
-rw-r--r--engines/kyra/text_lol.h2
-rw-r--r--engines/kyra/timer_mr.cpp2
-rw-r--r--engines/lastexpress/data/animation.cpp300
-rw-r--r--engines/lastexpress/data/animation.h114
-rw-r--r--engines/lastexpress/data/archive.cpp115
-rw-r--r--engines/lastexpress/data/archive.h75
-rw-r--r--engines/lastexpress/data/background.cpp141
-rw-r--r--engines/lastexpress/data/background.h81
-rw-r--r--engines/lastexpress/data/cursor.cpp144
-rw-r--r--engines/lastexpress/data/cursor.h93
-rw-r--r--engines/lastexpress/data/font.cpp205
-rw-r--r--engines/lastexpress/data/font.h82
-rw-r--r--engines/lastexpress/data/scene.cpp292
-rw-r--r--engines/lastexpress/data/scene.h245
-rw-r--r--engines/lastexpress/data/sequence.cpp482
-rw-r--r--engines/lastexpress/data/sequence.h205
-rw-r--r--engines/lastexpress/data/snd.cpp141
-rw-r--r--engines/lastexpress/data/snd.h97
-rw-r--r--engines/lastexpress/data/subtitle.cpp244
-rw-r--r--engines/lastexpress/data/subtitle.h79
-rw-r--r--engines/lastexpress/debug.cpp1178
-rw-r--r--engines/lastexpress/debug.h106
-rw-r--r--engines/lastexpress/detection.cpp211
-rw-r--r--engines/lastexpress/drawable.h (renamed from tools/sci/scriptdump.cpp)37
-rw-r--r--engines/lastexpress/entities/abbot.cpp1910
-rw-r--r--engines/lastexpress/entities/abbot.h225
-rw-r--r--engines/lastexpress/entities/alexei.cpp2002
-rw-r--r--engines/lastexpress/entities/alexei.h213
-rw-r--r--engines/lastexpress/entities/alouan.cpp504
-rw-r--r--engines/lastexpress/entities/alouan.h139
-rw-r--r--engines/lastexpress/entities/anna.cpp4032
-rw-r--r--engines/lastexpress/entities/anna.h252
-rw-r--r--engines/lastexpress/entities/august.cpp3536
-rw-r--r--engines/lastexpress/entities/august.h275
-rw-r--r--engines/lastexpress/entities/boutarel.cpp1260
-rw-r--r--engines/lastexpress/entities/boutarel.h188
-rw-r--r--engines/lastexpress/entities/chapters.cpp1820
-rw-r--r--engines/lastexpress/entities/chapters.h166
-rw-r--r--engines/lastexpress/entities/cooks.cpp571
-rw-r--r--engines/lastexpress/entities/cooks.h109
-rw-r--r--engines/lastexpress/entities/coudert.cpp4179
-rw-r--r--engines/lastexpress/entities/coudert.h229
-rw-r--r--engines/lastexpress/entities/entity.cpp486
-rw-r--r--engines/lastexpress/entities/entity.h800
-rw-r--r--engines/lastexpress/entities/entity39.cpp103
-rw-r--r--engines/lastexpress/entities/entity39.h78
-rw-r--r--engines/lastexpress/entities/entity_intern.h528
-rw-r--r--engines/lastexpress/entities/francois.cpp1295
-rw-r--r--engines/lastexpress/entities/francois.h170
-rw-r--r--engines/lastexpress/entities/gendarmes.cpp620
-rw-r--r--engines/lastexpress/entities/gendarmes.h99
-rw-r--r--engines/lastexpress/entities/hadija.cpp532
-rw-r--r--engines/lastexpress/entities/hadija.h144
-rw-r--r--engines/lastexpress/entities/ivo.cpp829
-rw-r--r--engines/lastexpress/entities/ivo.h177
-rw-r--r--engines/lastexpress/entities/kahina.cpp1528
-rw-r--r--engines/lastexpress/entities/kahina.h166
-rw-r--r--engines/lastexpress/entities/kronos.cpp892
-rw-r--r--engines/lastexpress/entities/kronos.h138
-rw-r--r--engines/lastexpress/entities/mahmud.cpp839
-rw-r--r--engines/lastexpress/entities/mahmud.h153
-rw-r--r--engines/lastexpress/entities/max.cpp628
-rw-r--r--engines/lastexpress/entities/max.h129
-rw-r--r--engines/lastexpress/entities/mertens.cpp4113
-rw-r--r--engines/lastexpress/entities/mertens.h220
-rw-r--r--engines/lastexpress/entities/milos.cpp1805
-rw-r--r--engines/lastexpress/entities/milos.h175
-rw-r--r--engines/lastexpress/entities/mmeboutarel.cpp1301
-rw-r--r--engines/lastexpress/entities/mmeboutarel.h164
-rw-r--r--engines/lastexpress/entities/pascale.cpp1232
-rw-r--r--engines/lastexpress/entities/pascale.h165
-rw-r--r--engines/lastexpress/entities/rebecca.cpp1838
-rw-r--r--engines/lastexpress/entities/rebecca.h230
-rw-r--r--engines/lastexpress/entities/salko.cpp642
-rw-r--r--engines/lastexpress/entities/salko.h149
-rw-r--r--engines/lastexpress/entities/servers0.cpp1039
-rw-r--r--engines/lastexpress/entities/servers0.h172
-rw-r--r--engines/lastexpress/entities/servers1.cpp787
-rw-r--r--engines/lastexpress/entities/servers1.h167
-rw-r--r--engines/lastexpress/entities/sophie.cpp279
-rw-r--r--engines/lastexpress/entities/sophie.h98
-rw-r--r--engines/lastexpress/entities/tables.cpp221
-rw-r--r--engines/lastexpress/entities/tables.h77
-rw-r--r--engines/lastexpress/entities/tatiana.cpp2272
-rw-r--r--engines/lastexpress/entities/tatiana.h235
-rw-r--r--engines/lastexpress/entities/train.cpp575
-rw-r--r--engines/lastexpress/entities/train.h95
-rw-r--r--engines/lastexpress/entities/vassili.cpp589
-rw-r--r--engines/lastexpress/entities/vassili.h110
-rw-r--r--engines/lastexpress/entities/verges.cpp1898
-rw-r--r--engines/lastexpress/entities/verges.h182
-rw-r--r--engines/lastexpress/entities/vesna.cpp1161
-rw-r--r--engines/lastexpress/entities/vesna.h176
-rw-r--r--engines/lastexpress/entities/yasmin.cpp490
-rw-r--r--engines/lastexpress/entities/yasmin.h142
-rw-r--r--engines/lastexpress/eventhandler.h53
-rw-r--r--engines/lastexpress/game/action.cpp1966
-rw-r--r--engines/lastexpress/game/action.h135
-rw-r--r--engines/lastexpress/game/beetle.cpp517
-rw-r--r--engines/lastexpress/game/beetle.h118
-rw-r--r--engines/lastexpress/game/entities.cpp2744
-rw-r--r--engines/lastexpress/game/entities.h380
-rw-r--r--engines/lastexpress/game/fight.cpp1587
-rw-r--r--engines/lastexpress/game/fight.h269
-rw-r--r--engines/lastexpress/game/inventory.cpp599
-rw-r--r--engines/lastexpress/game/inventory.h180
-rw-r--r--engines/lastexpress/game/logic.cpp593
-rw-r--r--engines/lastexpress/game/logic.h92
-rw-r--r--engines/lastexpress/game/menu.cpp1538
-rw-r--r--engines/lastexpress/game/menu.h210
-rw-r--r--engines/lastexpress/game/object.cpp109
-rw-r--r--engines/lastexpress/game/object.h92
-rw-r--r--engines/lastexpress/game/savegame.cpp564
-rw-r--r--engines/lastexpress/game/savegame.h288
-rw-r--r--engines/lastexpress/game/savepoint.cpp296
-rw-r--r--engines/lastexpress/game/savepoint.h151
-rw-r--r--engines/lastexpress/game/scenes.cpp1195
-rw-r--r--engines/lastexpress/game/scenes.h120
-rw-r--r--engines/lastexpress/game/sound.cpp1734
-rw-r--r--engines/lastexpress/game/sound.h343
-rw-r--r--engines/lastexpress/game/state.cpp82
-rw-r--r--engines/lastexpress/game/state.h657
-rw-r--r--engines/lastexpress/graphics.cpp166
-rw-r--r--engines/lastexpress/graphics.h76
-rw-r--r--engines/lastexpress/helpers.h102
-rw-r--r--engines/lastexpress/lastexpress.cpp310
-rw-r--r--engines/lastexpress/lastexpress.h153
-rw-r--r--engines/lastexpress/module.mk73
-rw-r--r--engines/lastexpress/resource.cpp248
-rw-r--r--engines/lastexpress/resource.h72
-rw-r--r--engines/lastexpress/shared.h1716
-rw-r--r--engines/lure/debugger.cpp2
-rw-r--r--engines/lure/hotspots.cpp23
-rw-r--r--engines/lure/hotspots.h2
-rw-r--r--engines/lure/lure.h6
-rw-r--r--engines/lure/res.cpp6
-rw-r--r--engines/lure/res_struct.cpp2
-rw-r--r--engines/lure/scripts.cpp10
-rw-r--r--engines/lure/sound.cpp6
-rw-r--r--engines/m4/console.cpp14
-rw-r--r--engines/m4/converse.cpp5
-rw-r--r--engines/m4/dialogs.cpp2
-rw-r--r--engines/m4/m4.cpp4
-rw-r--r--engines/m4/m4.h3
-rw-r--r--engines/m4/mads_logic.cpp4
-rw-r--r--engines/m4/mads_menus.cpp2
-rw-r--r--engines/m4/mads_views.cpp1
-rw-r--r--engines/made/made.cpp1
-rw-r--r--engines/made/made.h7
-rw-r--r--engines/made/music.cpp6
-rw-r--r--engines/made/screen.cpp2
-rw-r--r--engines/mohawk/console.cpp17
-rw-r--r--engines/mohawk/graphics.cpp83
-rw-r--r--engines/mohawk/graphics.h1
-rw-r--r--engines/mohawk/mohawk.h9
-rw-r--r--engines/mohawk/myst.cpp5
-rw-r--r--engines/mohawk/myst_scripts.cpp9
-rw-r--r--engines/mohawk/riven.cpp8
-rw-r--r--engines/mohawk/riven.h2
-rw-r--r--engines/mohawk/riven_cursors.h296
-rw-r--r--engines/mohawk/riven_external.cpp334
-rw-r--r--engines/mohawk/riven_external.h3
-rw-r--r--engines/mohawk/riven_saveload.cpp2
-rw-r--r--engines/mohawk/riven_scripts.cpp12
-rw-r--r--engines/mohawk/sound.cpp54
-rw-r--r--engines/mohawk/sound.h13
-rw-r--r--engines/parallaction/balloons.cpp4
-rw-r--r--engines/parallaction/disk_br.cpp2
-rw-r--r--engines/parallaction/gfxbase.cpp2
-rw-r--r--engines/parallaction/gui_ns.cpp4
-rw-r--r--engines/parallaction/parallaction.cpp2
-rw-r--r--engines/parallaction/parallaction.h5
-rw-r--r--engines/parallaction/parallaction_br.cpp3
-rw-r--r--engines/parallaction/sound_br.cpp4
-rw-r--r--engines/parallaction/staticres.cpp399
-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/queen.h4
-rw-r--r--engines/queen/talk.cpp6
-rw-r--r--engines/saga/actor.cpp388
-rw-r--r--engines/saga/actor.h74
-rw-r--r--engines/saga/actor_walk.cpp42
-rw-r--r--engines/saga/animation.cpp128
-rw-r--r--engines/saga/animation.h33
-rw-r--r--engines/saga/detection.cpp6
-rw-r--r--engines/saga/events.cpp127
-rw-r--r--engines/saga/events.h26
-rw-r--r--engines/saga/font.cpp78
-rw-r--r--engines/saga/font.h13
-rw-r--r--engines/saga/gfx.cpp33
-rw-r--r--engines/saga/gfx.h7
-rw-r--r--engines/saga/image.cpp112
-rw-r--r--engines/saga/interface.cpp228
-rw-r--r--engines/saga/interface.h11
-rw-r--r--engines/saga/introproc_ihnm.cpp20
-rw-r--r--engines/saga/introproc_ite.cpp146
-rw-r--r--engines/saga/isomap.cpp172
-rw-r--r--engines/saga/isomap.h21
-rw-r--r--engines/saga/itedata.cpp2
-rw-r--r--engines/saga/itedata.h8
-rw-r--r--engines/saga/music.cpp53
-rw-r--r--engines/saga/music.h8
-rw-r--r--engines/saga/objectmap.cpp173
-rw-r--r--engines/saga/objectmap.h38
-rw-r--r--engines/saga/palanim.cpp147
-rw-r--r--engines/saga/palanim.h19
-rw-r--r--engines/saga/puzzle.cpp10
-rw-r--r--engines/saga/resource.cpp62
-rw-r--r--engines/saga/resource.h2
-rw-r--r--engines/saga/resource_res.cpp131
-rw-r--r--engines/saga/saga.cpp64
-rw-r--r--engines/saga/saga.h163
-rw-r--r--engines/saga/saveload.cpp11
-rw-r--r--engines/saga/scene.cpp383
-rw-r--r--engines/saga/scene.h81
-rw-r--r--engines/saga/script.cpp160
-rw-r--r--engines/saga/script.h92
-rw-r--r--engines/saga/sfuncs.cpp40
-rw-r--r--engines/saga/sfuncs_ihnm.cpp10
-rw-r--r--engines/saga/shorten.cpp23
-rw-r--r--engines/saga/sndres.cpp57
-rw-r--r--engines/saga/sndres.h11
-rw-r--r--engines/saga/sound.cpp3
-rw-r--r--engines/saga/sound.h1
-rw-r--r--engines/saga/sprite.cpp244
-rw-r--r--engines/saga/sprite.h38
-rw-r--r--engines/saga/sthread.cpp26
-rw-r--r--engines/savestate.cpp6
-rw-r--r--engines/savestate.h5
-rw-r--r--engines/sci/console.cpp209
-rw-r--r--engines/sci/console.h7
-rw-r--r--engines/sci/detection.cpp39
-rw-r--r--engines/sci/detection_tables.h86
-rw-r--r--engines/sci/engine/kernel.cpp8
-rw-r--r--engines/sci/engine/kernel.h12
-rw-r--r--engines/sci/engine/kernel_tables.h876
-rw-r--r--engines/sci/engine/kfile.cpp33
-rw-r--r--engines/sci/engine/kgraphics.cpp264
-rw-r--r--engines/sci/engine/kmisc.cpp39
-rw-r--r--engines/sci/engine/kparse.cpp15
-rw-r--r--engines/sci/engine/kpathing.cpp2
-rw-r--r--engines/sci/engine/kscripts.cpp1
-rw-r--r--engines/sci/engine/kstring.cpp32
-rw-r--r--engines/sci/engine/kvideo.cpp2
-rw-r--r--engines/sci/engine/savegame.cpp112
-rw-r--r--engines/sci/engine/savegame.h17
-rw-r--r--engines/sci/engine/script.cpp7
-rw-r--r--engines/sci/engine/script.h23
-rw-r--r--engines/sci/engine/script_patches.cpp230
-rw-r--r--engines/sci/engine/seg_manager.cpp20
-rw-r--r--engines/sci/engine/segment.h2
-rw-r--r--engines/sci/engine/state.h1
-rw-r--r--engines/sci/engine/static_selectors.cpp32
-rw-r--r--engines/sci/engine/vm.cpp17
-rw-r--r--engines/sci/engine/workarounds.cpp14
-rw-r--r--engines/sci/graphics/animate.cpp118
-rw-r--r--engines/sci/graphics/animate.h12
-rw-r--r--engines/sci/graphics/compare.cpp22
-rw-r--r--engines/sci/graphics/controls.cpp23
-rw-r--r--engines/sci/graphics/cursor.cpp106
-rw-r--r--engines/sci/graphics/cursor.h13
-rw-r--r--engines/sci/graphics/font.cpp2
-rw-r--r--engines/sci/graphics/frameout.cpp231
-rw-r--r--engines/sci/graphics/frameout.h11
-rw-r--r--engines/sci/graphics/menu.cpp2
-rw-r--r--engines/sci/graphics/paint16.cpp8
-rw-r--r--engines/sci/graphics/picture.cpp6
-rw-r--r--engines/sci/graphics/ports.cpp2
-rw-r--r--engines/sci/graphics/robot.cpp169
-rw-r--r--engines/sci/graphics/robot.h1
-rw-r--r--engines/sci/graphics/screen.cpp12
-rw-r--r--engines/sci/graphics/text16.cpp79
-rw-r--r--engines/sci/graphics/text16.h10
-rw-r--r--engines/sci/graphics/transitions.cpp6
-rw-r--r--engines/sci/graphics/view.cpp33
-rw-r--r--engines/sci/graphics/view.h1
-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.cpp53
-rw-r--r--engines/sci/resource.h2
-rw-r--r--engines/sci/resource_audio.cpp11
-rw-r--r--engines/sci/sci.cpp122
-rw-r--r--engines/sci/sci.h10
-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/gm_names.h220
-rw-r--r--engines/sci/sound/drivers/map-mt32-to-gm.h447
-rw-r--r--engines/sci/sound/drivers/midi.cpp48
-rw-r--r--engines/sci/sound/drivers/mididriver.h25
-rw-r--r--engines/sci/sound/drivers/pcjr.cpp8
-rw-r--r--engines/sci/sound/music.cpp37
-rw-r--r--engines/sci/sound/music.h2
-rw-r--r--engines/sci/sound/soundcmd.cpp9
-rw-r--r--engines/scumm/actor.cpp81
-rw-r--r--engines/scumm/actor.h15
-rw-r--r--engines/scumm/akos.cpp8
-rw-r--r--engines/scumm/boxes.cpp4
-rw-r--r--engines/scumm/charset.cpp537
-rw-r--r--engines/scumm/charset.h37
-rw-r--r--engines/scumm/cursor.cpp33
-rw-r--r--engines/scumm/debugger.cpp5
-rw-r--r--engines/scumm/detection.cpp7
-rw-r--r--engines/scumm/detection_tables.h9
-rw-r--r--engines/scumm/dialogs.cpp18
-rw-r--r--engines/scumm/dialogs.h3
-rw-r--r--engines/scumm/file_nes.cpp2
-rw-r--r--engines/scumm/gfx.cpp291
-rw-r--r--engines/scumm/gfx.h63
-rw-r--r--engines/scumm/gfx_towns.cpp522
-rw-r--r--engines/scumm/he/logic_he.cpp26
-rw-r--r--engines/scumm/he/logic_he.h8
-rw-r--r--engines/scumm/he/palette_he.cpp4
-rw-r--r--engines/scumm/he/resource_he.cpp291
-rw-r--r--engines/scumm/he/resource_he.h2
-rw-r--r--engines/scumm/he/script_v100he.cpp7
-rw-r--r--engines/scumm/he/script_v60he.cpp7
-rw-r--r--engines/scumm/he/script_v70he.cpp1
-rw-r--r--engines/scumm/he/script_v72he.cpp9
-rw-r--r--engines/scumm/he/script_v80he.cpp1
-rw-r--r--engines/scumm/he/script_v90he.cpp55
-rw-r--r--engines/scumm/he/sound_he.cpp6
-rw-r--r--engines/scumm/he/wiz_he.cpp5
-rw-r--r--engines/scumm/he/wiz_he.h3
-rw-r--r--engines/scumm/imuse/imuse_player.cpp7
-rw-r--r--engines/scumm/insane/insane.cpp6
-rw-r--r--engines/scumm/insane/insane_ben.cpp2
-rw-r--r--engines/scumm/insane/insane_enemy.cpp4
-rw-r--r--engines/scumm/module.mk2
-rw-r--r--engines/scumm/object.cpp12
-rw-r--r--engines/scumm/palette.cpp67
-rw-r--r--engines/scumm/player_nes.h2
-rw-r--r--engines/scumm/player_pce.cpp324
-rw-r--r--engines/scumm/player_towns.cpp580
-rw-r--r--engines/scumm/player_towns.h119
-rw-r--r--engines/scumm/player_v2.cpp660
-rw-r--r--engines/scumm/player_v2.h264
-rw-r--r--engines/scumm/player_v2base.cpp657
-rw-r--r--engines/scumm/player_v2base.h148
-rw-r--r--engines/scumm/player_v2cms.cpp1146
-rw-r--r--engines/scumm/player_v2cms.h166
-rw-r--r--engines/scumm/resource.cpp13
-rw-r--r--engines/scumm/room.cpp5
-rw-r--r--engines/scumm/saveload.cpp48
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script.cpp66
-rw-r--r--engines/scumm/script_v0.cpp7
-rw-r--r--engines/scumm/script_v2.cpp3
-rw-r--r--engines/scumm/script_v4.cpp59
-rw-r--r--engines/scumm/script_v5.cpp76
-rw-r--r--engines/scumm/script_v6.cpp15
-rw-r--r--engines/scumm/scumm-md5.h16
-rw-r--r--engines/scumm/scumm.cpp159
-rw-r--r--engines/scumm/scumm.h74
-rw-r--r--engines/scumm/scumm_v0.h5
-rw-r--r--engines/scumm/scumm_v2.h2
-rw-r--r--engines/scumm/smush/codec47.cpp4
-rw-r--r--engines/scumm/sound.cpp7
-rw-r--r--engines/scumm/sound.h1
-rw-r--r--engines/scumm/string.cpp39
-rw-r--r--engines/scumm/vars.cpp6
-rw-r--r--engines/scumm/verbs.cpp62
-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/sky/sky.h4
-rw-r--r--engines/sword1/control.cpp8
-rw-r--r--engines/sword1/detection.cpp7
-rw-r--r--engines/sword1/logic.cpp4
-rw-r--r--engines/sword1/resman.cpp11
-rw-r--r--engines/sword1/router.cpp20
-rw-r--r--engines/sword1/screen.cpp10
-rw-r--r--engines/sword1/sound.cpp2
-rw-r--r--engines/sword1/sword1.cpp20
-rw-r--r--engines/sword1/sword1.h5
-rw-r--r--engines/sword1/text.cpp6
-rw-r--r--engines/sword2/controls.cpp6
-rw-r--r--engines/sword2/mouse.cpp2
-rw-r--r--engines/sword2/music.cpp13
-rw-r--r--engines/sword2/protocol.cpp4
-rw-r--r--engines/sword2/render.cpp4
-rw-r--r--engines/sword2/resman.cpp9
-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/sword2/sword2.h4
-rw-r--r--engines/sword25/detection.cpp160
-rw-r--r--engines/sword25/fmv/movieplayer.cpp228
-rw-r--r--engines/sword25/fmv/movieplayer.h156
-rw-r--r--engines/sword25/fmv/movieplayer_script.cpp163
-rw-r--r--engines/sword25/fmv/theora_decoder.cpp499
-rw-r--r--engines/sword25/fmv/theora_decoder.h158
-rw-r--r--engines/sword25/fmv/yuvtorgba.cpp247
-rw-r--r--engines/sword25/fmv/yuvtorgba.h56
-rw-r--r--engines/sword25/gfx/animation.cpp680
-rw-r--r--engines/sword25/gfx/animation.h219
-rw-r--r--engines/sword25/gfx/animationdescription.cpp65
-rw-r--r--engines/sword25/gfx/animationdescription.h103
-rw-r--r--engines/sword25/gfx/animationresource.cpp252
-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.h64
-rw-r--r--engines/sword25/gfx/bitmap.cpp183
-rw-r--r--engines/sword25/gfx/bitmap.h189
-rw-r--r--engines/sword25/gfx/bitmapresource.cpp62
-rw-r--r--engines/sword25/gfx/bitmapresource.h212
-rw-r--r--engines/sword25/gfx/dynamicbitmap.cpp155
-rw-r--r--engines/sword25/gfx/dynamicbitmap.h78
-rw-r--r--engines/sword25/gfx/fontresource.cpp138
-rw-r--r--engines/sword25/gfx/fontresource.h134
-rw-r--r--engines/sword25/gfx/framecounter.cpp68
-rw-r--r--engines/sword25/gfx/framecounter.h99
-rw-r--r--engines/sword25/gfx/graphicengine.cpp494
-rw-r--r--engines/sword25/gfx/graphicengine.h392
-rw-r--r--engines/sword25/gfx/graphicengine_script.cpp1305
-rw-r--r--engines/sword25/gfx/image/art.cpp2653
-rw-r--r--engines/sword25/gfx/image/art.h279
-rw-r--r--engines/sword25/gfx/image/image.h222
-rw-r--r--engines/sword25/gfx/image/pngloader.cpp255
-rw-r--r--engines/sword25/gfx/image/pngloader.h93
-rw-r--r--engines/sword25/gfx/image/renderedimage.cpp397
-rw-r--r--engines/sword25/gfx/image/renderedimage.h121
-rw-r--r--engines/sword25/gfx/image/swimage.cpp115
-rw-r--r--engines/sword25/gfx/image/swimage.h99
-rw-r--r--engines/sword25/gfx/image/vectorimage.cpp636
-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.cpp111
-rw-r--r--engines/sword25/gfx/panel.h73
-rw-r--r--engines/sword25/gfx/renderobject.cpp517
-rw-r--r--engines/sword25/gfx/renderobject.h525
-rw-r--r--engines/sword25/gfx/renderobjectmanager.cpp151
-rw-r--r--engines/sword25/gfx/renderobjectmanager.h129
-rw-r--r--engines/sword25/gfx/renderobjectptr.h79
-rw-r--r--engines/sword25/gfx/renderobjectregistry.cpp51
-rw-r--r--engines/sword25/gfx/renderobjectregistry.h57
-rw-r--r--engines/sword25/gfx/rootrenderobject.h72
-rw-r--r--engines/sword25/gfx/screenshot.cpp185
-rw-r--r--engines/sword25/gfx/screenshot.h51
-rw-r--r--engines/sword25/gfx/staticbitmap.cpp185
-rw-r--r--engines/sword25/gfx/staticbitmap.h81
-rw-r--r--engines/sword25/gfx/text.cpp358
-rw-r--r--engines/sword25/gfx/text.h169
-rw-r--r--engines/sword25/gfx/timedrenderobject.cpp52
-rw-r--r--engines/sword25/gfx/timedrenderobject.h59
-rw-r--r--engines/sword25/input/inputengine.cpp283
-rw-r--r--engines/sword25/input/inputengine.h322
-rw-r--r--engines/sword25/input/inputengine_script.cpp297
-rw-r--r--engines/sword25/kernel/common.h60
-rw-r--r--engines/sword25/kernel/filesystemutil.cpp87
-rw-r--r--engines/sword25/kernel/filesystemutil.h101
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.cpp152
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.h82
-rw-r--r--engines/sword25/kernel/kernel.cpp211
-rw-r--r--engines/sword25/kernel/kernel.h202
-rw-r--r--engines/sword25/kernel/kernel_script.cpp550
-rw-r--r--engines/sword25/kernel/log.cpp214
-rw-r--r--engines/sword25/kernel/log.h134
-rw-r--r--engines/sword25/kernel/objectregistry.h173
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.cpp101
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.h70
-rw-r--r--engines/sword25/kernel/persistable.h53
-rw-r--r--engines/sword25/kernel/persistenceblock.h123
-rw-r--r--engines/sword25/kernel/persistenceservice.cpp420
-rw-r--r--engines/sword25/kernel/persistenceservice.h78
-rw-r--r--engines/sword25/kernel/resmanager.cpp303
-rw-r--r--engines/sword25/kernel/resmanager.h175
-rw-r--r--engines/sword25/kernel/resource.cpp59
-rw-r--r--engines/sword25/kernel/resource.h110
-rw-r--r--engines/sword25/kernel/resservice.h74
-rw-r--r--engines/sword25/kernel/service.h75
-rw-r--r--engines/sword25/math/geometry.cpp49
-rw-r--r--engines/sword25/math/geometry.h56
-rw-r--r--engines/sword25/math/geometry_script.cpp492
-rw-r--r--engines/sword25/math/line.h198
-rw-r--r--engines/sword25/math/polygon.cpp491
-rw-r--r--engines/sword25/math/polygon.h267
-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.h (renamed from tools/sci/sciunpack.h)61
-rw-r--r--engines/sword25/math/vertex.cpp86
-rw-r--r--engines/sword25/math/vertex.h189
-rw-r--r--engines/sword25/math/walkregion.cpp401
-rw-r--r--engines/sword25/math/walkregion.h113
-rw-r--r--engines/sword25/module.mk106
-rw-r--r--engines/sword25/package/packagemanager.cpp276
-rw-r--r--engines/sword25/package/packagemanager.h205
-rw-r--r--engines/sword25/package/packagemanager_script.cpp211
-rw-r--r--engines/sword25/script/lua_extensions.cpp75
-rw-r--r--engines/sword25/script/luabindhelper.cpp427
-rw-r--r--engines/sword25/script/luabindhelper.h119
-rw-r--r--engines/sword25/script/luacallback.cpp173
-rw-r--r--engines/sword25/script/luacallback.h72
-rw-r--r--engines/sword25/script/luascript.cpp557
-rw-r--r--engines/sword25/script/luascript.h109
-rw-r--r--engines/sword25/script/script.h96
-rw-r--r--engines/sword25/sfx/soundengine.cpp270
-rw-r--r--engines/sword25/sfx/soundengine.h263
-rw-r--r--engines/sword25/sfx/soundengine_script.cpp365
-rw-r--r--engines/sword25/sword25.cpp202
-rw-r--r--engines/sword25/sword25.h96
-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.cpp1087
-rw-r--r--engines/sword25/util/lua/lapi.h16
-rw-r--r--engines/sword25/util/lua/lauxlib.cpp652
-rw-r--r--engines/sword25/util/lua/lauxlib.h174
-rw-r--r--engines/sword25/util/lua/lbaselib.cpp654
-rw-r--r--engines/sword25/util/lua/lcode.cpp839
-rw-r--r--engines/sword25/util/lua/lcode.h76
-rw-r--r--engines/sword25/util/lua/ldblib.cpp397
-rw-r--r--engines/sword25/util/lua/ldebug.cpp622
-rw-r--r--engines/sword25/util/lua/ldebug.h33
-rw-r--r--engines/sword25/util/lua/ldo.cpp518
-rw-r--r--engines/sword25/util/lua/ldo.h57
-rw-r--r--engines/sword25/util/lua/ldump.cpp164
-rw-r--r--engines/sword25/util/lua/lfunc.cpp174
-rw-r--r--engines/sword25/util/lua/lfunc.h34
-rw-r--r--engines/sword25/util/lua/lgc.cpp711
-rw-r--r--engines/sword25/util/lua/lgc.h110
-rw-r--r--engines/sword25/util/lua/linit.cpp38
-rw-r--r--engines/sword25/util/lua/liolib.cpp553
-rw-r--r--engines/sword25/util/lua/llex.cpp461
-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.cpp273
-rw-r--r--engines/sword25/util/lua/lmem.cpp86
-rw-r--r--engines/sword25/util/lua/lmem.h49
-rw-r--r--engines/sword25/util/lua/loadlib.cpp664
-rw-r--r--engines/sword25/util/lua/lobject.cpp214
-rw-r--r--engines/sword25/util/lua/lobject.h381
-rw-r--r--engines/sword25/util/lua/lopcodes.cpp102
-rw-r--r--engines/sword25/util/lua/lopcodes.h268
-rw-r--r--engines/sword25/util/lua/loslib.cpp243
-rw-r--r--engines/sword25/util/lua/lparser.cpp1339
-rw-r--r--engines/sword25/util/lua/lparser.h82
-rw-r--r--engines/sword25/util/lua/lstate.cpp214
-rw-r--r--engines/sword25/util/lua/lstate.h169
-rw-r--r--engines/sword25/util/lua/lstring.cpp111
-rw-r--r--engines/sword25/util/lua/lstring.h31
-rw-r--r--engines/sword25/util/lua/lstrlib.cpp868
-rw-r--r--engines/sword25/util/lua/ltable.cpp593
-rw-r--r--engines/sword25/util/lua/ltable.h40
-rw-r--r--engines/sword25/util/lua/ltablib.cpp279
-rw-r--r--engines/sword25/util/lua/ltm.cpp75
-rw-r--r--engines/sword25/util/lua/ltm.h54
-rw-r--r--engines/sword25/util/lua/lua.h388
-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.cpp225
-rw-r--r--engines/sword25/util/lua/lundump.h36
-rw-r--r--engines/sword25/util/lua/lvm.cpp763
-rw-r--r--engines/sword25/util/lua/lvm.h36
-rw-r--r--engines/sword25/util/lua/lzio.cpp82
-rw-r--r--engines/sword25/util/lua/lzio.h67
-rw-r--r--engines/sword25/util/lua/print.cpp227
-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.cpp112
-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.cpp1665
-rw-r--r--engines/sword25/util/pluto/pluto.h25
-rw-r--r--engines/sword25/util/pluto/plzio.cpp76
-rw-r--r--engines/sword25/util/pluto/pptest.cpp95
-rw-r--r--engines/sword25/util/pluto/pptest.lua168
-rw-r--r--engines/sword25/util/pluto/puptest.cpp81
-rw-r--r--engines/sword25/util/pluto/puptest.lua93
-rw-r--r--engines/teenagent/animation.cpp2
-rw-r--r--engines/teenagent/teenagent.h2
-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.cpp8
-rw-r--r--engines/tinsel/coroutine.cpp86
-rw-r--r--engines/tinsel/coroutine.h45
-rw-r--r--engines/tinsel/debugger.cpp3
-rw-r--r--engines/tinsel/detection_tables.h89
-rw-r--r--engines/tinsel/dialogs.cpp33
-rw-r--r--engines/tinsel/graphics.cpp6
-rw-r--r--engines/tinsel/module.mk1
-rw-r--r--engines/tinsel/music.cpp12
-rw-r--r--engines/tinsel/pdisplay.cpp18
-rw-r--r--engines/tinsel/rince.cpp4
-rw-r--r--engines/tinsel/scene.h6
-rw-r--r--engines/tinsel/sched.cpp12
-rw-r--r--engines/tinsel/sound.cpp21
-rw-r--r--engines/tinsel/text.cpp2
-rw-r--r--engines/tinsel/text.h2
-rw-r--r--engines/tinsel/tinlib.cpp55
-rw-r--r--engines/tinsel/tinsel.cpp12
-rw-r--r--engines/tinsel/tinsel.h4
-rw-r--r--engines/toon/anim.cpp738
-rw-r--r--engines/toon/anim.h196
-rw-r--r--engines/toon/audio.cpp629
-rw-r--r--engines/toon/audio.h177
-rw-r--r--engines/toon/character.cpp1043
-rw-r--r--engines/toon/character.h148
-rw-r--r--engines/toon/conversation.cpp49
-rw-r--r--engines/toon/conversation.h50
-rw-r--r--engines/toon/detection.cpp273
-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.cpp285
-rw-r--r--engines/toon/font.h53
-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.cpp114
-rw-r--r--engines/toon/movie.h61
-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.h153
-rw-r--r--engines/toon/script_func.cpp1186
-rw-r--r--engines/toon/script_func.h174
-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.cpp4548
-rw-r--r--engines/toon/toon.h424
-rw-r--r--engines/touche/midi.cpp5
-rw-r--r--engines/touche/touche.h4
-rw-r--r--engines/tucker/tucker.h2
-rw-r--r--graphics/VectorRenderer.cpp4
-rw-r--r--graphics/VectorRendererSpec.cpp2
-rw-r--r--graphics/colormasks.h24
-rw-r--r--graphics/jpeg.cpp2
-rw-r--r--graphics/pict.cpp104
-rw-r--r--graphics/pict.h8
-rw-r--r--graphics/sjis.cpp58
-rw-r--r--graphics/sjis.h49
-rw-r--r--graphics/surface.cpp60
-rw-r--r--graphics/video/avi_decoder.cpp26
-rw-r--r--graphics/video/avi_decoder.h5
-rw-r--r--graphics/video/codecs/indeo3.cpp80
-rw-r--r--graphics/video/codecs/qdm2.cpp14
-rw-r--r--graphics/video/coktel_decoder.cpp46
-rw-r--r--graphics/video/flic_decoder.cpp4
-rw-r--r--graphics/video/qt_decoder.cpp68
-rw-r--r--graphics/video/qt_decoder.h15
-rw-r--r--graphics/video/smk_decoder.cpp130
-rw-r--r--graphics/video/smk_decoder.h5
-rw-r--r--gui/EditTextWidget.cpp2
-rw-r--r--gui/GuiManager.cpp3
-rw-r--r--gui/KeysDialog.h3
-rw-r--r--gui/ListWidget.cpp12
-rw-r--r--gui/ThemeEngine.cpp90
-rw-r--r--gui/ThemeEngine.h12
-rw-r--r--gui/ThemeEval.h4
-rw-r--r--gui/ThemeParser.cpp42
-rw-r--r--gui/Tooltip.cpp18
-rw-r--r--gui/Tooltip.h9
-rw-r--r--gui/browser.cpp5
-rw-r--r--gui/browser.h6
-rw-r--r--gui/browser_osx.mm3
-rw-r--r--gui/credits.h26
-rw-r--r--gui/editable.cpp5
-rw-r--r--gui/editable.h2
-rw-r--r--gui/launcher.cpp27
-rw-r--r--gui/message.cpp6
-rw-r--r--gui/options.cpp109
-rw-r--r--gui/saveload.cpp2
-rw-r--r--gui/themes/scummmodern.zipbin181317 -> 181827 bytes
-rw-r--r--gui/themes/scummmodern/scummmodern_layout.stx6
-rw-r--r--gui/themes/scummmodern/scummmodern_layout_lowres.stx2
-rwxr-xr-xgui/themes/scummtheme.py36
-rw-r--r--gui/themes/translations.datbin75042 -> 80935 bytes
-rw-r--r--icons/scummvm.infobin17326 -> 17326 bytes
-rw-r--r--po/POTFILES7
-rw-r--r--po/ca_ES.po734
-rw-r--r--po/de_DE.po560
-rw-r--r--po/es_ES.po682
-rw-r--r--po/fr_FR.po620
-rw-r--r--po/hu_HU.po519
-rw-r--r--po/it_IT.po597
-rw-r--r--po/module.mk4
-rw-r--r--po/ru_RU.po521
-rw-r--r--po/scummvm.pot492
-rw-r--r--po/uk_UA.po615
-rw-r--r--ports.mk13
-rw-r--r--sound/audiocd.cpp4
-rw-r--r--sound/decoders/adpcm.cpp13
-rw-r--r--sound/decoders/adpcm.h17
-rw-r--r--sound/decoders/flac.cpp9
-rw-r--r--sound/decoders/mac_snd.cpp2
-rw-r--r--sound/decoders/vorbis.cpp5
-rw-r--r--sound/fmopl.cpp6
-rw-r--r--sound/mididrv.cpp66
-rw-r--r--sound/mididrv.h10
-rw-r--r--sound/mods/tfmx.cpp42
-rw-r--r--sound/module.mk1
-rw-r--r--sound/softsynth/cms.cpp376
-rw-r--r--sound/softsynth/cms.h92
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_audio.cpp30
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_euphony.cpp12
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp38
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_pc98_driver.h6
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp41
-rw-r--r--sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h16
-rw-r--r--sound/softsynth/mt32/mt32_file.cpp18
-rw-r--r--sound/softsynth/mt32/mt32_file.h2
-rw-r--r--sound/softsynth/mt32/tables.cpp2
-rw-r--r--sound/softsynth/opl/mame.cpp6
-rw-r--r--test/common/array.h30
-rw-r--r--test/common/rational.h20
-rw-r--r--test/common/str.h15
-rw-r--r--tools/README53
-rw-r--r--tools/create_drascula/staticdata.h1150
-rw-r--r--tools/create_hugo/create_hugo.cpp72
-rw-r--r--tools/create_hugo/create_hugo.h2
-rw-r--r--tools/create_hugo/dists/msvc9/create_hugo.vcproj4
-rw-r--r--tools/create_hugo/enums.h11
-rw-r--r--tools/create_hugo/staticdata.h1066
-rw-r--r--tools/create_hugo/staticfont.h187
-rw-r--r--tools/create_hugo/staticintro.h35
-rw-r--r--tools/create_hugo/staticparser.h11
-rw-r--r--tools/create_kyradat/create_kyradat.cpp7
-rw-r--r--tools/create_kyradat/extract.cpp55
-rw-r--r--tools/create_kyradat/extract.h1
-rw-r--r--tools/create_kyradat/games.cpp3
-rw-r--r--tools/create_kyradat/md5.cpp3
-rw-r--r--tools/create_kyradat/pak.cpp3
-rw-r--r--tools/create_kyradat/search.cpp3
-rw-r--r--tools/create_kyradat/tables.cpp5
-rw-r--r--tools/create_kyradat/util.cpp3
-rw-r--r--tools/create_lure/create_lure_dat.cpp3
-rw-r--r--tools/create_lure/create_lure_dat.h4
-rw-r--r--tools/create_lure/process_actions.cpp3
-rw-r--r--tools/create_msvc/create_msvc.cpp24
-rw-r--r--tools/create_teenagent/create_teenagent.cpp3
-rw-r--r--tools/create_teenagent/md5.cpp3
-rw-r--r--tools/create_toon/create_toon.cpp166
-rw-r--r--tools/create_toon/create_toon.h47
-rw-r--r--tools/create_toon/dists/msvc9/create_toon.sln20
-rw-r--r--tools/create_toon/dists/msvc9/create_toon.vcproj187
-rw-r--r--tools/create_toon/module.mk10
-rw-r--r--tools/create_toon/staticdata.h324
-rw-r--r--tools/create_translations/create_translations.cpp22
-rw-r--r--tools/create_translations/po_parser.cpp111
-rw-r--r--tools/create_translations/po_parser.h12
-rwxr-xr-xtools/credits.pl50
-rw-r--r--tools/sci/classes.cpp76
-rw-r--r--tools/sci/graphics_png.h112
-rw-r--r--tools/sci/listwords.cpp85
-rw-r--r--tools/sci/old_objects.cpp679
-rw-r--r--tools/sci/old_objects.h72
-rw-r--r--tools/sci/scitrace.asm2
-rw-r--r--tools/sci/sciunpack.cpp473
-rw-r--r--tools/sci/vocabdump.cpp70
-rw-r--r--tools/scumm-md5.txt16
-rw-r--r--tools/skycpt/AsciiCptCompile.cpp8
-rw-r--r--tools/skycpt/AsciiCptCompile.vcproj2
-rw-r--r--tools/skycpt/KmpSearch.h4
-rw-r--r--tools/skycpt/README21
-rw-r--r--tools/skycpt/TextFile.cpp7
-rw-r--r--tools/skycpt/TextFile.h4
-rw-r--r--tools/skycpt/cptcompiler.cpp5
-rw-r--r--tools/skycpt/skycpt-engine.patch67
-rw-r--r--tools/skycpt/stdafx.h4
-rwxr-xr-xtools/svnpropset.sh6
-rw-r--r--tools/themeparser.py268
-rwxr-xr-xtools/update-version.pl3
1324 files changed, 182040 insertions, 23479 deletions
diff --git a/AUTHORS b/AUTHORS
index 76ff47e9db..fcd14d0b12 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -99,6 +99,11 @@ ScummVM Team
Scott Thomas
Jordi Vilalta Prat
+ Hugo:
+ Arnaud Boutonne
+ Oystein Eftevaag
+ Eugene Sandulenko
+
Kyra:
Torbjorn Andersson - VQA Player
Oystein Eftevaag
@@ -106,6 +111,11 @@ ScummVM Team
Gregory Montoir
Johannes Schickel
+ Last Express:
+ Matthew Hoops
+ Jordi Vilalta Prat
+ Julien Templier
+
Lure:
Paul Gilbert
@@ -158,6 +168,9 @@ ScummVM Team
Filippos Karapetis
Joost Peters
+ Toon:
+ Sylvain Dupont
+
Touche:
Gregory Montoir
@@ -172,7 +185,7 @@ ScummVM Team
Dreamcast:
Marcus Comstedt
- GP2X:
+ GPH Devices (GP2X, GP2XWiz & Caanoo):
John Willis
iPhone:
@@ -190,6 +203,9 @@ ScummVM Team
Nintendo DS:
Neil Millstone
+ OpenPandora:
+ John Willis
+
PocketPC / WinCE:
Nicolas Bacca - (retired)
Kostas Nakos
@@ -449,6 +465,9 @@ Special thanks to
Patrick Combet - For the original Gobliiins ADL player
Ivan Dubrov - For contributing the initial version of the Gobliiins
engine
+ Henrik Engqvist - For generously providing hosting for our buildbot, SVN
+ repository, planet and doxygen sites as well as tons of
+ HD space
DOSBox Team - For their awesome OPL2 and OPL3 emulator
Till Kresslein - For design of modern ScummVM GUI
Jezar - For his freeverb filter implementation
@@ -482,3 +501,13 @@ Special thanks to
John Young, Colin Smythe and especially Terry Pratchett himself for
sharing the source code of Discworld I & II with us.
+ Emilio de Paz Aragon from Alcachofa Soft for sharing the source code of
+ Drascula: The Vampire Strikes Back with us and his generosity with
+ freewaring the game.
+
+ David P. Gray from Gray Design Associate for sharing the source code of
+ the Hugo trilogy.
+
+ Broken Sword 2.5 team for providing sources of their engine and their
+ great support.
+
diff --git a/Makefile b/Makefile
index ad2f8fcf9f..e8c8a17291 100644
--- a/Makefile
+++ b/Makefile
@@ -34,8 +34,14 @@ ifeq "$(HAVE_GCC)" "1"
# being helpful.
#CXXFLAGS+= -Wmissing-format-attribute
- # Disable RTTI and exceptions, and enable checking of pointers returned by "new"
- CXXFLAGS+= -fno-rtti -fno-exceptions -fcheck-new
+ # Disable RTTI and exceptions
+ CXXFLAGS+= -fno-rtti -fno-exceptions
+
+ifneq "$(HAVE_CLANG)" "1"
+ # enable checking of pointers returned by "new", but only when we do not
+ # build with clang
+ CXXFLAGS+= -fcheck-new
+endif
endif
ifeq "$(HAVE_CLANG)" "1"
diff --git a/Makefile.common b/Makefile.common
index cf8626ce3c..5abfc2e784 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -230,7 +230,7 @@ dist-src: \
DIST_FILES_DOCS:=$(addprefix $(srcdir)/,AUTHORS COPYING COPYING.BSD COPYING.LGPL COPYRIGHT NEWS README)
# Themes files
-DIST_FILES_THEMES=scummmodern.zip
+DIST_FILES_THEMES=scummmodern.zip scummclassic.zip
ifdef USE_TRANSLATION
DIST_FILES_THEMES+=translations.dat
endif
@@ -262,6 +262,9 @@ endif
ifdef ENABLE_TEENAGENT
DIST_FILES_ENGINEDATA+=teenagent.dat
endif
+ifdef ENABLE_TOON
+DIST_FILES_ENGINEDATA+=toon.dat
+endif
DIST_FILES_ENGINEDATA:=$(addprefix $(srcdir)/dists/engine-data/,$(DIST_FILES_ENGINEDATA))
# pred.dic is currently only used for the AGI engine
diff --git a/NEWS b/NEWS
index 0e69339fd9..d988f9b17a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,75 @@
For a more comprehensive changelog for the latest experimental SVN code, see:
http://scummvm.svn.sourceforge.net/viewvc/scummvm/?view=log
+1.3.0 (????-??-??)
+ New Games:
+ - Added support for Backyard Baseball.
+ - Added support for Backyard Baseball 2001.
+
+ Drascula:
+ - Added German and French subtitles in the Von Braun cutscene (#3069981:
+ no subtitles in scene with "von Braun").
+ - Improved French translation of the game.
+
+ SCI:
+ - Improved support for non-English versions of games.
+
+ SCUMM:
+ - Improved support for FM-TOWNS versions of games.
+
+1.2.1 (2010-??-??)
+ Groovie:
+ - Fixed a regression that made the Russian version of T7G crash.
+
+1.2.0 (2010-10-15)
+ New Games:
+ - Added support for Fascination.
+
+ New Games (Sierra SCI0 - SCI1.1):
+ - Added support for Castle of Dr. Brain (EGA and VGA).
+ - Added support for Codename: ICEMAN.
+ - Added support for Conquests of Camelot.
+ - Added support for Conquests of the Longbow (EGA and VGA).
+ - Added support for EcoQuest: The Search for Cetus.
+ - Added support for EcoQuest 2: Lost Secret of the Rainforest.
+ - Added support for Freddy Pharkas: Frontier Pharmacist.
+ - Added support for Hoyle's Book of Games 1.
+ - Added support for Hoyle's Book of Games 2.
+ - Added support for Hoyle's Book of Games 3 (EGA and VGA).
+ - Added support for Hoyle Classic Card Games.
+ - Added support for Jones in the Fast Lane.
+ - Added support for King's Quest I (SCI remake).
+ - Added support for King's Quest IV (SCI version).
+ - Added support for King's Quest V (EGA and VGA).
+ - Added support for King's Quest VI (low and hi res).
+ - Added support for Laura Bow: The Colonel's Bequest.
+ - Added support for Laura Bow 2: The Dagger of Amon Ra.
+ - Added support for Leisure Suit Larry 1 (SCI remake) (EGA and VGA).
+ - Added support for Leisure Suit Larry 2.
+ - Added support for Leisure Suit Larry 3.
+ - Added support for Leisure Suit Larry 5 (EGA and VGA).
+ - Added support for Leisure Suit Larry 6 (low res).
+ - Added support for Mixed-up Fairy Tales.
+ - Added support for Mixed-up Mother Goose.
+ - Added support for Pepper's Adventures in Time.
+ - Added support for Police Quest I (SCI remake).
+ - Added support for Police Quest II.
+ - Added support for Police Quest III (EGA and VGA).
+ - Added support for Quest for Glory I/Hero's Quest.
+ - Added support for Quest for Glory I VGA remake.
+ - Added support for Quest for Glory II.
+ - Added support for Quest for Glory III.
+ - Added support for Slater & Charlie go camping.
+ - Added support for Space Quest I (SCI remake) (EGA and VGA).
+ - Added support for Space Quest III.
+ - Added support for Space Quest IV (EGA and VGA).
+ - Added support for Space Quest V.
+ - Added support for The Island of Dr. Brain.
-1.2.0 (????-??-??)
New Ports:
- Added Android port.
- Added Dingux port.
- Added Caanoo port (based on the GP2XWiz port).
+ - Added OpenPandora port.
General:
- Removed the outdated PalmOS port.
@@ -23,6 +87,11 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Added support of MIDI devices.
- Added support for accurate Tandy sound emulation. Switched to it as default.
+ Broken Sword 2:
+ - Fixed missing speech in some cutscenes.
+ - Fixed a memory leak that would eventually cause the game to hang.
+ (#2976008 - BS2: Game lockup in British Museum)
+
Drascula:
- Fixed number of GFX glitches.
- Made many cutscenes smoother.
@@ -37,6 +106,12 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Implemented formerly missing recreation of some in game items.
- Added support for playing Kyrandia 3 with the original CD file layout.
+ LURE:
+ - Fixed bug where Goewin could get stuck in the Weregate.
+ - Fixed issue with Ratpouch repeatedly moving between two rooms.
+ - Fix for Goewin losing her schedule after Were-cave.
+ - Fix for player getting stuck in sewer exit room.
+
Parallaction:
- Made part one of The Big Red Adventure completable.
@@ -58,15 +133,16 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
GameCube port:
- Added support for DVDs with the ISO9660 file system.
-
+
GP2X port:
- - Added support for dynamic engine plugins.
+ - Added support for dynamic engine plugins (experimental).
+ - Reworked control system and better touchscreen support.
-1.1.2 (????-??-??)
- Broken Sword 2
- - Fixed missing speech in some cutscenes.
- - Fixed a memory leak that would eventually cause the game to hang.
- (#2976008 - BS2: Game lockup in British Museum)
+ GP2XWiz/Caanoo port:
+ - Improved downscale code to minimise 'tearing' corruption.
+ - Reworked control system and better touchscreen support.
+ - Renamed backend from GP2XWIZ to GPH to better reflect
+ the supported devices.
1.1.1 (2010-05-02)
New Ports:
@@ -87,7 +163,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
- Fixed several memory leaks.
- Corrected problems in the handling of followers when blocked from performing
actions by closed doors between rooms.
- - Solved issues with Goewin not always correctly following the player out of the caves
+ - Solved issues with Goewin not always correctly following the player out of the caves.
Tinsel:
- Fix video playback regression in Discworld 2.
@@ -141,7 +217,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
The Secret of Monkey Island. (GSoC Task)
- Fixed some other bugs related to game versions for the Amiga.
- Added support for original save/load dialog in MM NES.
- - Added support for savepoint passcodes for Sega CD MI1 via debugger command 'passcode'
+ - Added support for savepoint passcodes for Sega CD MI1 via debugger command 'passcode'.
- Added support for Kanji rendering in Japanese version of Monkey Island Sega CD.
1.0.0 (2009-11-15)
diff --git a/README b/README
index 9c60055429..fd900ce310 100644
--- a/README
+++ b/README
@@ -71,25 +71,37 @@ files. The clever part about this: ScummVM just replaces the executables
shipped with the game, allowing you to play them on systems for which
they were never designed!
-Some of the adventures ScummVM supports include Adventure Soft's Simon
-the Sorcerer 1 and 2; Revolution's Beneath A Steel Sky, Broken Sword I
-and II; Flight of the Amazon Queen; Wyrmkeep's Inherit the Earth; Coktel
-Vision's Gobliiins; Westwood Studios' The Legend of Kyrandia and games
-based on LucasArts' SCUMM (Script Creation Utility for Maniac Mansion)
-system such as Monkey Island, Day of the Tentacle, Sam and Max and more.
-You can find a thorough list with details on which games are supported
-and how well on the compatibility page. ScummVM is continually
-improving, so check back often.
+Originally it was designed to run LucasArts' SCUMM games, such as Maniac
+Mansion, Monkey Island, Day of the Tentacle or Sam and Max. SCUMM stands
+for 'Script Creation Utility for Maniac Mansion', which was the first
+game for which LucasArts designed this system. And much later it gave
+its name to ScummVM ('VM' meaning Virtual Machine).
+
+Over time support for a lot of non-SCUMM games has been added, and
+ScummVM now also supports many of Sierra's AGI and SCI games (such as King's
+Quest 1-6, Space Quest 1-5, ...), Discworld 1 and 2, Simon the Sorcerer 1 and
+2, Beneath A Steel Sky, Lure of the Temptress, Broken Sword I and II, Flight of
+the Amazon Queen, Gobliiins 1-3, The Legend of Kyrandia series, many of
+Humongous Entertainment's children's SCUMM games (including Freddi Fish and
+Putt Putt games) and many more. You can find a full list with details on which
+adventures are supported and how well on the compatibility page. ScummVM is
+continually improving, so check back often.
Among the systems on which you can play those games are regular desktop
computers (running Windows, Linux, Mac OS X, ...), game consoles
(Dreamcast, Nintendo DS & Wii, PS2, PSP, ...), smartphones (Android,
iPhone, PocketPC, Symbian ...) and more.
-At this time ScummVM should be considered beta software, and is still
-under heavy development. Be aware that whilst we attempt to make sure
-that many games can be completed with few major bugs, crashes can
-happen.
+At this time ScummVM is still under heavy development. Be aware that
+whilst we attempt to make sure that many games can be completed with few
+major bugs, crashes can happen and we offer no warranty. That being said,
+some of the games have been supported for a long time and should work
+fine with any recent stable release. You can get a feeling of how well
+each game is working in ScummVM by looking at the compatibility page.
+Actually if you browse a bit around you might discover that ScummVM is
+even being used commercially to re-release some of the supported games on
+modern platforms. This shows that several companies are happy with the
+quality of the software and how well it can run some of the games.
If you enjoy ScummVM feel free to donate using the PayPal button on the
ScummVM homepage. This will help us buy utilities needed to develop
@@ -256,6 +268,8 @@ Other Games:
Musketeer [touche]
SCUMM Games by Humongous Entertainment:
+ Backyard Baseball [baseball]
+ Backyard Baseball 2001 [baseball2001]
Backyard Football [football]
Big Thinkers First Grade [thinker1]
Big Thinkers Kindergarten [thinkerk]
@@ -309,8 +323,6 @@ these at your own risk, and please do not file bug reports about them.
If you want the latest updates on game compatibility, visit our web site
and view the compatibility chart.
- Backyard Baseball [baseball]
- Backyard Baseball 2001 [baseball2001]
Backyard Baseball 2003 [baseball2003]
Backyard Football 2002 [football2002]
Backyard Soccer [soccer]
@@ -382,9 +394,9 @@ in the previous paragraph.
3.3) Maniac Mansion NES notes:
---- -------------------------
-Supported versions are English GB (E), French (F), German (G), Swedish
-(SW) and English US (U). ScummVM requires just the PRG section to run
-and not the whole ROM.
+Supported versions are English GB (E), French (F), German (G), Italian (I),
+Swedish (SW) and English US (U). ScummVM requires just the PRG section
+to run and not the whole ROM.
In order to get the game working, you will have to strip out the first
16 bytes from the ROM you are trying to work with. Any hex editor will
@@ -392,7 +404,7 @@ work as long as you are able to copy/paste. After you open the ROM with
the hex editor, copy everything from the second row (17th byte) to the
end. After you do this, paste it to a new hex file. Name the new file
"Maniac Mansion (XX).prg" while XX stands for the version you are
-working with (E, F, G, SW, or U). The final size should be exactly
+working with (E, F, G, I, SW, or U). The final size should be exactly
262144 bytes.
If you add the game manually make sure that the platform is set to NES.
diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp
index 0616713eab..c8d19a57a5 100644
--- a/backends/events/default/default-events.cpp
+++ b/backends/events/default/default-events.cpp
@@ -169,6 +169,7 @@ bool DefaultEventManager::pollEvent(Common::Event &event) {
// key pressed. A better fix would be for engines to stop
// making invalid assumptions about ascii values.
event.kbd.ascii = Common::KEYCODE_BACKSPACE;
+ _currentKeyDown.ascii = Common::KEYCODE_BACKSPACE;
}
break;
diff --git a/backends/fs/psp/psp-fs.cpp b/backends/fs/psp/psp-fs.cpp
index 4857a385b7..5b3d298001 100644
--- a/backends/fs/psp/psp-fs.cpp
+++ b/backends/fs/psp/psp-fs.cpp
@@ -248,18 +248,18 @@ AbstractFSNode *PSPFilesystemNode::getParent() const {
Common::SeekableReadStream *PSPFilesystemNode::createReadStream() {
const uint32 READ_BUFFER_SIZE = 1024;
-
+
Common::SeekableReadStream *stream = PspIoStream::makeFromPath(getPath(), false);
-
- return new PspIoBufferedReadStream(stream, READ_BUFFER_SIZE, DisposeAfterUse::YES);
+
+ return new Common::BufferedSeekableReadStream(stream, READ_BUFFER_SIZE, DisposeAfterUse::YES);
}
Common::WriteStream *PSPFilesystemNode::createWriteStream() {
const uint32 WRITE_BUFFER_SIZE = 1024;
-
+
Common::WriteStream *stream = PspIoStream::makeFromPath(getPath(), true);
-
- return new PspIoBufferedWriteStream(stream, WRITE_BUFFER_SIZE, DisposeAfterUse::YES);
+
+ return new Common::BufferedWriteStream(stream, WRITE_BUFFER_SIZE, DisposeAfterUse::YES);
}
#endif //#ifdef __PSP__
diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp
index 3b0737ba63..10b80a0639 100644
--- a/backends/fs/psp/psp-stream.cpp
+++ b/backends/fs/psp/psp-stream.cpp
@@ -43,17 +43,17 @@
#ifdef DEBUG_BUFFERS
void printBuffer(byte *ptr, uint32 len) {
uint32 printLen = len <= 10 ? len : 10;
-
+
for (int i = 0; i < printLen; i++) {
- PSP_INFO_PRINT("%x ", ptr[i]);
+ PSP_INFO_PRINT("%x ", ptr[i]);
}
-
+
if (len > 10) {
PSP_INFO_PRINT("... ");
for (int i = len - 10; i < len; i++)
PSP_INFO_PRINT("%x ", ptr[i]);
}
-
+
PSP_INFO_PRINT("\n");
}
#endif
@@ -62,7 +62,7 @@ void printBuffer(byte *ptr, uint32 len) {
PspIoStream::PspIoStream(const Common::String &path, bool writeMode)
: _handle(0), _path(path), _fileSize(0), _writeMode(writeMode),
- _physicalPos(0), _pos(0), _eos(false), _error(false),
+ _physicalPos(0), _pos(0), _eos(false), _error(false),
_errorSuspend(0), _errorSource(0), _errorPos(0), _errorHandle(0), _suspendCount(0) {
DEBUG_ENTER_FUNC();
@@ -74,11 +74,11 @@ PspIoStream::~PspIoStream() {
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
-
+
PowerMan.unregisterForSuspend(this); // Unregister with powermanager to be suspended
// Must do this before fclose() or resume() will reopen.
sceIoClose(_handle);
-
+
PowerMan.endCriticalSection();
}
@@ -87,7 +87,7 @@ PspIoStream::~PspIoStream() {
*/
void *PspIoStream::open() {
DEBUG_ENTER_FUNC();
-
+
if (PowerMan.beginCriticalSection()) {
// No need to open? Just return the _handle resume() already opened
PSP_DEBUG_PRINT_FUNC("suspended\n");
@@ -97,13 +97,13 @@ void *PspIoStream::open() {
if (!_handle) {
_error = true;
_handle = NULL;
- }
-
+ }
+
// Get the file size. This way is much faster than going to the end of the file and back
SceIoStat stat;
sceIoGetstat(_path.c_str(), &stat);
_fileSize = *((uint32 *)(void *)&stat.st_size); // 4GB file (32 bits) is big enough for us
-
+
PSP_DEBUG_PRINT("%s filesize[%d]\n", _path.c_str(), _fileSize);
PowerMan.registerForSuspend(this); // Register with the powermanager to be suspended
@@ -115,7 +115,7 @@ void *PspIoStream::open() {
bool PspIoStream::err() const {
DEBUG_ENTER_FUNC();
-
+
if (_error) // We dump since no printing to screen with suspend callback
PSP_ERROR("mem_error[%d], source[%d], suspend error[%d], pos[%d],"
"_errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
@@ -142,9 +142,9 @@ int32 PspIoStream::size() const {
}
bool PspIoStream::physicalSeekFromCur(int32 offset) {
-
+
int ret = sceIoLseek32(_handle, offset, PSP_SEEK_CUR);
-
+
if (ret < 0) {
_error = true;
PSP_ERROR("failed to seek in file[%s] to [%x]. Error[%x]\n", _path.c_str(), offset, ret);
@@ -158,7 +158,7 @@ bool PspIoStream::seek(int32 offs, int whence) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT_FUNC("offset[0x%x], whence[%d], _pos[0x%x], _physPos[0x%x]\n", offs, whence, _pos, _physicalPos);
_eos = false;
-
+
int32 posToSearchFor = 0;
switch (whence) {
case SEEK_CUR:
@@ -179,9 +179,9 @@ bool PspIoStream::seek(int32 offs, int whence) {
_eos = true;
return false;
}
-
+
_pos = posToSearchFor;
-
+
return true;
}
@@ -198,33 +198,33 @@ uint32 PspIoStream::read(void *ptr, uint32 len) {
if (len > lenRemainingInFile) {
len = lenRemainingInFile;
_eos = true;
- }
+ }
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
-
+
// check if we need to seek
if (_pos != _physicalPos)
PSP_DEBUG_PRINT("seeking from %x to %x\n", _physicalPos, _pos);
if (!physicalSeekFromCur(_pos - _physicalPos)) {
_error = true;
return 0;
- }
-
+ }
+
int ret = sceIoRead(_handle, ptr, len);
PowerMan.endCriticalSection();
-
+
_physicalPos += ret; // Update position
_pos = _physicalPos;
-
+
if (ret != (int)len) { // error
PSP_ERROR("sceIoRead returned [0x%x] instead of len[0x%x]\n", ret, len);
_error = true;
- _errorSource = 4;
+ _errorSource = 4;
}
return ret;
-}
+}
uint32 PspIoStream::write(const void *ptr, uint32 len) {
DEBUG_ENTER_FUNC();
@@ -234,7 +234,7 @@ uint32 PspIoStream::write(const void *ptr, uint32 len) {
return 0;
_eos = false; // we can't have eos with write
-
+
if (PowerMan.beginCriticalSection())
PSP_DEBUG_PRINT_FUNC("suspended\n");
@@ -244,11 +244,11 @@ uint32 PspIoStream::write(const void *ptr, uint32 len) {
_error = true;
return 0;
}
-
+
int ret = sceIoWrite(_handle, ptr, len);
-
+
PowerMan.endCriticalSection();
-
+
if (ret != (int)len) {
_error = true;
_errorSource = 5;
@@ -257,10 +257,10 @@ uint32 PspIoStream::write(const void *ptr, uint32 len) {
_physicalPos += ret;
_pos = _physicalPos;
-
+
if (_pos > _fileSize)
- _fileSize = _pos;
-
+ _fileSize = _pos;
+
return ret;
}
@@ -323,7 +323,7 @@ int PspIoStream::resume() {
// Resume our previous position if needed
if (_handle > 0 && _pos > 0) {
ret = sceIoLseek32(_handle, _pos, PSP_SEEK_SET);
-
+
_physicalPos = _pos;
if (ret < 0) { // Check for problem
diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h
index be3a1220de..d4497aa79f 100644
--- a/backends/fs/psp/psp-stream.h
+++ b/backends/fs/psp/psp-stream.h
@@ -33,22 +33,6 @@
#include "common/stream.h"
#include "common/str.h"
-class PspIoBufferedReadStream : public Common::BufferedSeekableReadStream {
-public:
- PspIoBufferedReadStream(SeekableReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES) : BufferedSeekableReadStream(parentStream, bufSize, disposeParentStream) {}
-protected:
- virtual void allocBuf(uint32 bufSize) { _buf = (byte *)memalign(64, bufSize); } // want 64 byte alignment for cache
- virtual void deallocBuf() { free(_buf); }
-};
-
-class PspIoBufferedWriteStream : public Common::BufferedWriteStream {
-public:
- PspIoBufferedWriteStream(WriteStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES) : BufferedWriteStream(parentStream, bufSize, disposeParentStream) {}
-protected:
- virtual void allocBuf(uint32 bufSize) { _buf = (byte *)memalign(64, bufSize); }
- virtual void deallocBuf() { free(_buf); }
-};
-
/**
* Class to handle special suspend/resume needs of PSP IO Streams
*/
@@ -61,7 +45,7 @@ protected:
int _physicalPos; // physical position in file
int _pos; // position. Sometimes virtual
bool _eos; // EOS flag
-
+
enum {
SuspendError = 2,
ResumeError = 3
@@ -74,9 +58,9 @@ protected:
int _errorPos;
SceUID _errorHandle;
int _suspendCount;
-
+
bool physicalSeekFromCur(int32 offset);
-
+
public:
/**
@@ -101,7 +85,7 @@ public:
virtual int32 size() const;
virtual bool seek(int32 offs, int whence = SEEK_SET);
virtual uint32 read(void *dataPtr, uint32 dataSize);
-
+
// for suspending
int suspend(); /* Suspendable interface (power manager) */
int resume(); /* " " */
diff --git a/backends/fs/stdiostream.cpp b/backends/fs/stdiostream.cpp
index 8845d796c6..d0600f41a6 100644
--- a/backends/fs/stdiostream.cpp
+++ b/backends/fs/stdiostream.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use FILE, fopen etc.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "backends/fs/stdiostream.h"
StdioStream::StdioStream(void *handle) : _handle(handle) {
diff --git a/backends/fs/windows/windows-fs-factory.cpp b/backends/fs/windows/windows-fs-factory.cpp
index c74868b40d..f7b9c837f2 100644
--- a/backends/fs/windows/windows-fs-factory.cpp
+++ b/backends/fs/windows/windows-fs-factory.cpp
@@ -23,6 +23,10 @@
*/
#if defined(WIN32)
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "backends/fs/windows/windows-fs-factory.h"
#include "backends/fs/windows/windows-fs.cpp"
diff --git a/backends/midi/alsa.cpp b/backends/midi/alsa.cpp
index 4f73d7384b..fd32777a1b 100644
--- a/backends/midi/alsa.cpp
+++ b/backends/midi/alsa.cpp
@@ -22,6 +22,9 @@
* $Id$
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#if defined(USE_ALSA)
diff --git a/backends/midi/camd.cpp b/backends/midi/camd.cpp
index 3486532549..7bf702de58 100644
--- a/backends/midi/camd.cpp
+++ b/backends/midi/camd.cpp
@@ -22,6 +22,9 @@
* $Id$
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#if defined(__amigaos4__)
diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp
index aa0ad75f0a..97db5cb292 100644
--- a/backends/midi/coreaudio.cpp
+++ b/backends/midi/coreaudio.cpp
@@ -24,6 +24,9 @@
#ifdef MACOSX
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
// HACK to disable deprecated warnings under Mac OS X 10.5.
// Apple depracted the AUGraphNewNode & AUGraphGetNodeInfo APIs
// in favor of the new AUGraphAddNode & AUGraphNodeInfo APIs.
diff --git a/backends/midi/coremidi.cpp b/backends/midi/coremidi.cpp
index 08f36a8b0f..bca16df61a 100644
--- a/backends/midi/coremidi.cpp
+++ b/backends/midi/coremidi.cpp
@@ -24,6 +24,9 @@
#ifdef MACOSX
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/config-manager.h"
#include "common/util.h"
#include "sound/musicplugin.h"
diff --git a/backends/midi/dmedia.cpp b/backends/midi/dmedia.cpp
index 8c006b2cd9..5e4088fa17 100644
--- a/backends/midi/dmedia.cpp
+++ b/backends/midi/dmedia.cpp
@@ -29,6 +29,9 @@
#if defined(IRIX)
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#include "common/util.h"
#include "common/config-manager.h"
diff --git a/backends/midi/seq.cpp b/backends/midi/seq.cpp
index e3d2c35b39..c0098742d0 100644
--- a/backends/midi/seq.cpp
+++ b/backends/midi/seq.cpp
@@ -28,6 +28,9 @@
* both the QuickTime support and (vkeybd http://www.alsa-project.org/~iwai/alsa.html)
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#if defined(USE_SEQ_MIDI)
diff --git a/backends/midi/stmidi.cpp b/backends/midi/stmidi.cpp
index b00188dfea..01e28aa5ca 100644
--- a/backends/midi/stmidi.cpp
+++ b/backends/midi/stmidi.cpp
@@ -36,6 +36,9 @@
#if defined __MINT__
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include <osbind.h>
#include "sound/mpu401.h"
#include "common/util.h"
diff --git a/backends/midi/timidity.cpp b/backends/midi/timidity.cpp
index f507f1e00a..d79a83809f 100644
--- a/backends/midi/timidity.cpp
+++ b/backends/midi/timidity.cpp
@@ -34,7 +34,12 @@
*
*/
-#if defined (UNIX)
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/scummsys.h"
+
+#if defined(USE_TIMIDITY)
#include "common/util.h"
#include "common/endian.h"
diff --git a/backends/midi/windows.cpp b/backends/midi/windows.cpp
index da44c40978..81b29219b4 100644
--- a/backends/midi/windows.cpp
+++ b/backends/midi/windows.cpp
@@ -24,6 +24,9 @@
#if defined(WIN32) && !defined(_WIN32_WCE)
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// winnt.h defines ARRAYSIZE, but we want our own one...
@@ -106,7 +109,7 @@ void MidiDriver_WIN::sysEx(const byte *msg, uint16 length) {
return;
if (WaitForSingleObject (_streamEvent, 2000) == WAIT_TIMEOUT) {
- warning ("Could not send SysEx - MMSYSTEM is still trying to send data.");
+ warning ("Could not send SysEx - MMSYSTEM is still trying to send data");
return;
}
diff --git a/backends/platform/android/README.build b/backends/platform/android/README.build
index 1c407bd469..f3fb77cbcf 100644
--- a/backends/platform/android/README.build
+++ b/backends/platform/android/README.build
@@ -82,7 +82,8 @@ Then build ScummVM:
export ANDROID_TOP=<root of built Android source>
- ./configure --backend=android --host=android --enable-zlib #and any other flags
+ ./configure --backend=android --host=android --enable-zlib --disable-timidity
+ # ... and any other configure flags you want
make scummvm.apk
This will build a "monolithic" ScummVM package, with the engines
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index f6af0fcff5..38f387b201 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -109,7 +109,7 @@ static void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) {
env->DeleteLocalRef(cls);
}
-// floating point. use sparingly.
+// floating point. use sparingly.
template <class T>
static inline T scalef(T in, float numerator, float denominator) {
return static_cast<float>(in) * numerator / denominator;
@@ -177,7 +177,6 @@ private:
GLESPaletteTexture* _game_texture;
int _shake_offset;
Common::Rect _focus_rect;
- bool _full_screen_dirty;
// Overlay layer
GLES4444Texture* _overlay_texture;
@@ -320,7 +319,6 @@ OSystem_Android::OSystem_Android(jobject am)
_fsFactory(new POSIXFilesystemFactory()),
_asset_archive(new AndroidAssetArchive(am)),
_shake_offset(0),
- _full_screen_dirty(false),
_event_queue_lock(createMutex()) {
}
@@ -400,7 +398,7 @@ static void ScummVM_audioMixCallback(JNIEnv* env, jobject self,
jsize len = env->GetArrayLength(jbuf);
jbyte* buf = env->GetByteArrayElements(jbuf, NULL);
if (buf == NULL) {
- warning("Unable to get Java audio byte array. Skipping.");
+ warning("Unable to get Java audio byte array. Skipping");
return;
}
Audio::MixerImpl* mixer =
@@ -862,6 +860,9 @@ void OSystem_Android::hideOverlay() {
void OSystem_Android::clearOverlay() {
ENTER("clearOverlay()");
_overlay_texture->fillBuffer(0);
+
+ // Shouldn't need this, but works around a 'blank screen' bug on Nexus1
+ updateScreen();
}
void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
@@ -887,6 +888,9 @@ void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
// This 'pitch' is pixels not bytes
_overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0]));
+
+ // Shouldn't need this, but works around a 'blank screen' bug on Nexus1?
+ updateScreen();
}
int16 OSystem_Android::getOverlayHeight() {
@@ -1135,7 +1139,7 @@ OSystem::MutexRef OSystem_Android::createMutex() {
pthread_mutex_t *mutex = new pthread_mutex_t;
if (pthread_mutex_init(mutex, &attr) != 0) {
- warning("pthread_mutex_init() failed!");
+ warning("pthread_mutex_init() failed");
delete mutex;
return NULL;
}
@@ -1144,18 +1148,18 @@ OSystem::MutexRef OSystem_Android::createMutex() {
void OSystem_Android::lockMutex(MutexRef mutex) {
if (pthread_mutex_lock((pthread_mutex_t*)mutex) != 0)
- warning("pthread_mutex_lock() failed!");
+ warning("pthread_mutex_lock() failed");
}
void OSystem_Android::unlockMutex(MutexRef mutex) {
if (pthread_mutex_unlock((pthread_mutex_t*)mutex) != 0)
- warning("pthread_mutex_unlock() failed!");
+ warning("pthread_mutex_unlock() failed");
}
void OSystem_Android::deleteMutex(MutexRef mutex) {
pthread_mutex_t* m = (pthread_mutex_t*)mutex;
if (pthread_mutex_destroy(m) != 0)
- warning("pthread_mutex_destroy() failed!");
+ warning("pthread_mutex_destroy() failed");
else
delete m;
}
@@ -1330,7 +1334,7 @@ static void ScummVM_enableZoning(JNIEnv* env, jobject self, jboolean enable) {
static void ScummVM_setSurfaceSize(JNIEnv* env, jobject self,
jint width, jint height) {
OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
- cpp_obj->setSurfaceSize(width, height);
+ cpp_obj->setSurfaceSize(width, height);
}
const static JNINativeMethod gMethods[] = {
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
index d39aa363ef..6986f3988d 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
@@ -23,6 +23,8 @@ import javax.microedition.khronos.egl.EGLSurface;
import java.io.File;
import java.util.concurrent.Semaphore;
+import java.util.Map;
+import java.util.LinkedHashMap;
// At least in Android 2.1, eglCreateWindowSurface() requires an
@@ -109,6 +111,51 @@ public class ScummVM implements SurfaceHolder.Callback {
}
}
+ // For debugging
+ private static final Map<String, Integer> attribs;
+ static {
+ attribs = new LinkedHashMap<String, Integer>();
+ attribs.put("CONFIG_ID", EGL10.EGL_CONFIG_ID);
+ attribs.put("BUFFER_SIZE", EGL10.EGL_BUFFER_SIZE);
+ attribs.put("RED_SIZE", EGL10.EGL_RED_SIZE);
+ attribs.put("GREEN_SIZE", EGL10.EGL_GREEN_SIZE);
+ attribs.put("BLUE_SIZE", EGL10.EGL_BLUE_SIZE);
+ attribs.put("ALPHA_SIZE", EGL10.EGL_ALPHA_SIZE);
+ //attribs.put("BIND_TO_RGB", EGL10.EGL_BIND_TO_TEXTURE_RGB);
+ //attribs.put("BIND_TO_RGBA", EGL10.EGL_BIND_TO_TEXTURE_RGBA);
+ attribs.put("CONFIG_CAVEAT", EGL10.EGL_CONFIG_CAVEAT);
+ attribs.put("DEPTH_SIZE", EGL10.EGL_DEPTH_SIZE);
+ attribs.put("LEVEL", EGL10.EGL_LEVEL);
+ attribs.put("MAX_PBUFFER_WIDTH", EGL10.EGL_MAX_PBUFFER_WIDTH);
+ attribs.put("MAX_PBUFFER_HEIGHT", EGL10.EGL_MAX_PBUFFER_HEIGHT);
+ attribs.put("MAX_PBUFFER_PIXELS", EGL10.EGL_MAX_PBUFFER_PIXELS);
+ //attribs.put("MAX_SWAP_INTERVAL", EGL10.EGL_MAX_SWAP_INTERVAL);
+ //attribs.put("MIN_SWAP_INTERVAL", EGL10.EGL_MIN_SWAP_INTERVAL);
+ attribs.put("NATIVE_RENDERABLE", EGL10.EGL_NATIVE_RENDERABLE);
+ attribs.put("NATIVE_VISUAL_ID", EGL10.EGL_NATIVE_VISUAL_ID);
+ attribs.put("NATIVE_VISUAL_TYPE", EGL10.EGL_NATIVE_VISUAL_TYPE);
+ attribs.put("SAMPLE_BUFFERS", EGL10.EGL_SAMPLE_BUFFERS);
+ attribs.put("SAMPLES", EGL10.EGL_SAMPLES);
+ attribs.put("STENCIL_SIZE", EGL10.EGL_STENCIL_SIZE);
+ attribs.put("SURFACE_TYPE", EGL10.EGL_SURFACE_TYPE);
+ attribs.put("TRANSPARENT_TYPE", EGL10.EGL_TRANSPARENT_TYPE);
+ attribs.put("TRANSPARENT_RED_VALUE", EGL10.EGL_TRANSPARENT_RED_VALUE);
+ attribs.put("TRANSPARENT_GREEN_VALUE", EGL10.EGL_TRANSPARENT_GREEN_VALUE);
+ attribs.put("TRANSPARENT_BLUE_VALUE", EGL10.EGL_TRANSPARENT_BLUE_VALUE);
+ }
+ private void dumpEglConfig(EGLConfig config) {
+ int[] value = new int[1];
+ for (Map.Entry<String, Integer> entry : attribs.entrySet()) {
+ egl.eglGetConfigAttrib(eglDisplay, config,
+ entry.getValue(), value);
+ if (value[0] == EGL10.EGL_NONE)
+ Log.d(LOG_TAG, entry.getKey() + ": NONE");
+ else
+ Log.d(LOG_TAG, String.format("%s: %d",
+ entry.getKey(), value[0]));
+ }
+ }
+
// Called by ScummVM thread (from initBackend)
private void createScummVMGLContext() {
egl = (EGL10)EGLContext.getEGL();
@@ -125,10 +172,75 @@ public class ScummVM implements SurfaceHolder.Callback {
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
num_config);
- eglConfig = configs[0];
+
+ if (false) {
+ Log.d(LOG_TAG,
+ String.format("Found %d EGL configurations.", numConfigs));
+ for (EGLConfig config : configs)
+ dumpEglConfig(config);
+ }
+
+ // Android's eglChooseConfig is busted in several versions and
+ // devices so we have to filter/rank the configs again ourselves.
+ eglConfig = chooseEglConfig(configs);
+ if (false) {
+ Log.d(LOG_TAG,
+ String.format("Chose EGL config from %d possibilities.", numConfigs));
+ dumpEglConfig(eglConfig);
+ }
eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
EGL10.EGL_NO_CONTEXT, null);
+ if (eglContext == EGL10.EGL_NO_CONTEXT)
+ throw new RuntimeException("Failed to create context");
+ }
+
+ private EGLConfig chooseEglConfig(EGLConfig[] configs) {
+ int best = 0;
+ int bestScore = -1;
+ int[] value = new int[1];
+ for (int i = 0; i < configs.length; i++) {
+ EGLConfig config = configs[i];
+ int score = 10000;
+ egl.eglGetConfigAttrib(eglDisplay, config,
+ EGL10.EGL_SURFACE_TYPE, value);
+ if ((value[0] & EGL10.EGL_WINDOW_BIT) == 0)
+ continue; // must have
+
+ egl.eglGetConfigAttrib(eglDisplay, config,
+ EGL10.EGL_CONFIG_CAVEAT, value);
+ if (value[0] != EGL10.EGL_NONE)
+ score -= 1000;
+
+ // Must be at least 555, but then smaller is better
+ final int[] colorBits = {EGL10.EGL_RED_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_ALPHA_SIZE};
+ for (int component : colorBits) {
+ egl.eglGetConfigAttrib(eglDisplay, config,
+ component, value);
+ if (value[0] >= 5)
+ score += 10; // boost if >5 bits accuracy
+ score -= value[0]; // penalize for wasted bits
+ }
+
+ egl.eglGetConfigAttrib(eglDisplay, config,
+ EGL10.EGL_DEPTH_SIZE, value);
+ score -= value[0]; // penalize for wasted bits
+
+ if (score > bestScore) {
+ best = i;
+ bestScore = score;
+ }
+ }
+
+ if (bestScore < 0) {
+ Log.e(LOG_TAG, "Unable to find an acceptable EGL config, expect badness.");
+ return configs[0];
+ }
+
+ return configs[best];
}
// Called by ScummVM thread
@@ -137,12 +249,13 @@ public class ScummVM implements SurfaceHolder.Callback {
try {
surfaceLock.acquire();
} catch (InterruptedException e) {
- Log.e(this.toString(),
- "Interrupted while waiting for surface lock", e);
+ Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e);
return;
}
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
nativeSurface, null);
+ if (eglSurface == EGL10.EGL_NO_SURFACE)
+ Log.e(LOG_TAG, "CreateWindowSurface failed!");
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
GL10 gl = (GL10)eglContext.getGL();
@@ -302,8 +415,8 @@ public class ScummVM implements SurfaceHolder.Callback {
if (buf_size < 0) {
int guess = AUDIO_FRAME_SIZE * sample_rate / 100; // 10ms of audio
Log.w(LOG_TAG, String.format(
- "Unable to get min audio buffer size (error %d). Guessing %dB.",
- buf_size, guess));
+ "Unable to get min audio buffer size (error %d). Guessing %dB.",
+ buf_size, guess));
buf_size = guess;
}
Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio",
diff --git a/backends/platform/android/video.cpp b/backends/platform/android/video.cpp
index d4c002fbd0..81a8f7fbc7 100644
--- a/backends/platform/android/video.cpp
+++ b/backends/platform/android/video.cpp
@@ -38,6 +38,9 @@
#include "backends/platform/android/video.h"
+// Unfortunately, Android devices are too varied to make broad assumptions :/
+#define TEXSUBIMAGE_IS_EXPENSIVE 0
+
#undef LOG_TAG
#define LOG_TAG "ScummVM-video"
@@ -158,13 +161,11 @@ void GLESTexture::allocBuffer(GLuint w, GLuint h) {
// later (perhaps with multiple TexSubImage2D operations).
CHECK_GL_ERROR();
glBindTexture(GL_TEXTURE_2D, _texture_name);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- CHECK_GL_ERROR();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
CHECK_GL_ERROR();
glTexImage2D(GL_TEXTURE_2D, 0, glFormat(),
@@ -177,6 +178,7 @@ void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
const void* buf, int pitch) {
ENTER("updateBuffer(%u, %u, %u, %u, %p, %d)", x, y, w, h, buf, pitch);
glBindTexture(GL_TEXTURE_2D, _texture_name);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
setDirtyRect(Common::Rect(x, y, x+w, y+h));
@@ -185,7 +187,25 @@ void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
glFormat(), glType(), buf);
} else {
// GLES removed the ability to specify pitch, so we
- // have to do this row by row.
+ // have to do this ourselves.
+ if (h == 0)
+ return;
+
+#if TEXSUBIMAGE_IS_EXPENSIVE
+ byte tmpbuf[w * h * bytesPerPixel()];
+ const byte* src = static_cast<const byte*>(buf);
+ byte* dst = tmpbuf;
+ GLuint count = h;
+ do {
+ memcpy(dst, src, w * bytesPerPixel());
+ dst += w * bytesPerPixel();
+ src += pitch;
+ } while (--count);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h,
+ glFormat(), glType(), tmpbuf);
+#else
+ // This version avoids the intermediate copy at the expense of
+ // repeat glTexSubImage2D calls. On some devices this is worse.
const byte* src = static_cast<const byte*>(buf);
do {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y,
@@ -193,16 +213,15 @@ void GLESTexture::updateBuffer(GLuint x, GLuint y, GLuint w, GLuint h,
++y;
src += pitch;
} while (--h);
+#endif
}
}
void GLESTexture::fillBuffer(byte x) {
- byte tmpbuf[_surface.h * _surface.w * bytesPerPixel()];
- memset(tmpbuf, 0, _surface.h * _surface.w * bytesPerPixel());
- glBindTexture(GL_TEXTURE_2D, _texture_name);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _surface.w, _surface.h,
- glFormat(), glType(), tmpbuf);
- setDirty();
+ int rowbytes = _surface.w * bytesPerPixel();
+ byte tmpbuf[_surface.h * rowbytes];
+ memset(tmpbuf, x, _surface.h * rowbytes);
+ updateBuffer(0, 0, _surface.w, _surface.h, tmpbuf, rowbytes);
}
void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
@@ -215,6 +234,7 @@ void GLESTexture::drawTexture(GLshort x, GLshort y, GLshort w, GLshort h) {
//glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
const GLint crop[4] = {0, _surface.h, _surface.w, -_surface.h};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glColor4ub(0xff, 0xff, 0xff, 0xff); // Android GLES bug?
glDrawTexiOES(x, y, 0, w, h);
} else
#endif
diff --git a/backends/platform/dc/selector.cpp b/backends/platform/dc/selector.cpp
index 0d9b931d2c..8fd12d66bf 100644
--- a/backends/platform/dc/selector.cpp
+++ b/backends/platform/dc/selector.cpp
@@ -272,7 +272,7 @@ static int findGames(Game *games, int max, bool use_ini)
games[curr_game].dir,
games[curr_game].language,
games[curr_game].platform, games, curr_game)) {
-
+
strcpy(games[curr_game].text, ge->description().c_str());
#if 0
printf("Registered game <%s> (l:%d p:%d) in <%s> <%s> because of <%s> <*>\n",
diff --git a/backends/platform/dingux/README.DINGUX b/backends/platform/dingux/README.DINGUX
new file mode 100644
index 0000000000..d867e02f03
--- /dev/null
+++ b/backends/platform/dingux/README.DINGUX
@@ -0,0 +1,68 @@
+ScummVM-DINGUX README
+==============================================================================
+
+Requirements
+============
+- Dingoo A320/A330
+- Dingux installed on SD card
+- Fixed Tremor libs in dingux rootfs (see below)
+
+Controls
+============
+- Dpad: move mouse cursor
+- Y: left mouse button click
+- A: '0' key
+- B: right mouse button click
+- X: '.' key (skips dialogue line in some engines)
+- Left Trigger: open global menu
+- Right Trigger: ESC button, scene skip in some engines
+- Select: opens virtual keyboard
+- Start: F5 key, game menu in some engines
+
+Installation from binaries
+==============================
+Mount your dingux SD card in your pc, then copy the directory "scummvm" found in
+the release package to a directory inside /pathtosdcard/local/dirofyourchoice
+(on windows it would be SDLETTER:\local\dirofyourchoice).
+At this point is sufficient to point your launcher (eg. gmenu2x) to scummvm.gpe
+file included into the scummvm directory you copied to the SD card, and then launch it.
+
+Building from binaries
+==============================
+* ToDO *
+
+Kernel and rootfs WARNINGS
+==============================
+All the dingux root images (rootfs) i found floating on the net have
+broken tremor libraries, which make scummvm crash in a bad way.
+One solution is to replace the libraries in your rootfs by injecting these fixed ones:
+http://hkzlab.ipv7.net/files/misc/dingux/dingux_fixed_tremor_libs.zip
+After having added these libs, scummvm should work on your standard dingux kernel,
+but this doesn't mean it will work perfectly:
+non-opendingux kernels doesn't have lcd double buffering leading to a lot of annoying
+tearing on screen.
+
+The best way to address all the problems in one shot, is to use an opendingux kernel for
+your dingoo, which has some interesting advantages:
+- The kernel gets updated and kept in sync with main linux tree
+- Double buffering and centering of screen image
+- Support for swap on compressed ram
+I have prepared a rootfs image with fixed libs and swap-on-zram to use together with opendingux kernels:
+http://hkzlab.ipv7.net/files/misc/dingux/opendingux/opendingux_kernel_rootfs.zip
+The kernel and rootfs images in the zip file is what i use for developing scummvm on dingux.
+
+BTW, i have built images for the A330 and A320 with ILI9325 controllers too,
+but these version aren't tested (i have an A320 with ILI9331, but if someone wants to
+donate an A330 to a poor scummvm developer so i can try the port there too... :P)
+
+If you need a launcher with opendingux cpu frequency scaler support, you can find gmenu2x here:
+http://www.treewalker.org/dingux/cpufreq.html
+
+I've also prepared a rootfs image for use on normal dingux kernels (non-opendingux ones).
+It's based on elta's rootfs image with just the fixed libs replaced.
+http://hkzlab.ipv7.net/files/misc/dingux/normal_dingux/rootfs_elta_fixtremor.zip
+I still raccomand the use of opendingux kernel + rootfs, but if you don't, this roofs
+image plus another kernel (eg. SiENcE's one) should be do the job.
+
+Enjoy
+
diff --git a/backends/platform/dingux/dingux-events.cpp b/backends/platform/dingux/dingux-events.cpp
index b6070f68ca..f9b519623d 100644
--- a/backends/platform/dingux/dingux-events.cpp
+++ b/backends/platform/dingux/dingux-events.cpp
@@ -149,7 +149,7 @@ bool OSystem_SDL_Dingux::remapKey(SDL_Event &ev, Common::Event &event) {
}
return true;
- } else if (ev.key.keysym.sym == BUT_A) { // virtual keyboard
+ } else if (ev.key.keysym.sym == BUT_A) { // key '0'
ev.key.keysym.sym = SDLK_0;
event.kbd.keycode = Common::KEYCODE_0;
diff --git a/backends/platform/dingux/dingux.mk b/backends/platform/dingux/dingux.mk
new file mode 100644
index 0000000000..882078fe46
--- /dev/null
+++ b/backends/platform/dingux/dingux.mk
@@ -0,0 +1,30 @@
+DINGUX_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
+
+bundle_name = dingux-dist/scummvm
+
+all: $(DINGUX_EXE_STRIPPED)
+
+$(DINGUX_EXE_STRIPPED): $(EXECUTABLE)
+ $(STRIP) $< -o $@
+
+dingux-distclean:
+ rm -rf $(bundle_name)
+ rm $(DINGUX_EXE_STRIPPED)
+
+dingux-dist: all
+ $(MKDIR) $(bundle_name)
+ $(MKDIR) $(bundle_name)/saves
+ $(STRIP) $(EXECUTABLE) -o $(bundle_name)/scummvm.elf
+ $(CP) $(DIST_FILES_THEMES) $(bundle_name)/
+ifdef DIST_FILES_ENGINEDATA
+ $(CP) $(DIST_FILES_ENGINEDATA) $(bundle_name)/
+endif
+ $(CP) $(DIST_FILES_DOCS) $(bundle_name)/
+ifdef DYNAMIC_MODULES
+ $(MKDIR) $(bundle_name)/plugins
+ $(CP) $(PLUGINS) $(bundle_name)/plugins
+ $(STRIP) $(bundle_name)/plugins/*
+endif
+ $(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/dingux/scummvm.gpe $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/dingux/README.DINGUX $(bundle_name)/
diff --git a/backends/platform/dingux/scummvm.gpe b/backends/platform/dingux/scummvm.gpe
new file mode 100644
index 0000000000..84ab0c6b95
--- /dev/null
+++ b/backends/platform/dingux/scummvm.gpe
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+HOME=`pwd`
+$HOME/scummvm.elf
+
diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile
index df7063c08a..db8e41af48 100644
--- a/backends/platform/ds/arm9/makefile
+++ b/backends/platform/ds/arm9/makefile
@@ -69,6 +69,8 @@ else
# TODO: Inherit the earth uses so much RAM that I have removed libmad in order to
# claw some back.
+
+
else
ifdef DS_BUILD_I
diff --git a/backends/platform/ds/arm9/source/touchkeyboard.cpp b/backends/platform/ds/arm9/source/touchkeyboard.cpp
index 71f4f93c27..581509f939 100644
--- a/backends/platform/ds/arm9/source/touchkeyboard.cpp
+++ b/backends/platform/ds/arm9/source/touchkeyboard.cpp
@@ -177,7 +177,7 @@ void drawText(int tx, int ty, const char *string, bool highlight) {
baseAddress[ty * 32 + tx + p] = baseValue | tile;
}
}
-
+
}
@@ -292,7 +292,7 @@ void drawAutoComplete() {
// When there's no completions on the bottom of the screen, it acts like a mouse pad
// so this text indicates that
drawText(11, 18, "MOUSE AREA", true);
-
+
} else {
@@ -303,10 +303,10 @@ void drawAutoComplete() {
for (int r = 0; r < autoCompleteCount; r++) {
int y = 12 + (r % 6) * 2;
int x = 0 + ((r / 6) * 16);
-
+
drawText(x, y, autoCompleteWord[r], selectedCompletion == r);
}
-
+
}
}
diff --git a/backends/platform/gp2x/build/README-GP2X b/backends/platform/gp2x/build/README-GP2X
index f95a974230..3c6591d369 100644
--- a/backends/platform/gp2x/build/README-GP2X
+++ b/backends/platform/gp2x/build/README-GP2X
@@ -1,72 +1,40 @@
-ScummVM - GP2X SPECIFIC README - HEAD SVN
+ScummVM - GP2X SPECIFIC README
------------------------------------------------------------------------
Contents:
* About the backend/port <#About_the_backendport>
- * Game compatability <#Game_compatibility>
- * Included engines <#Included_engines>
* Supported audio options <#Supported_audio_options>
- * Supported cut-scene options <#Supported_cut-scene_options>
- * Recent changes <#Recent_changes>
- * How to save <#How_to_save>
- * Controller mappings <#Controller_mappings>
- * Know issues <#Know_issues>
- * TODO's <#Major_TODOs>
- * Additional resources/links <#Additional_resourceslinks>
* Credits <#Credits>
------------------------------------------------------------------------
Please refer to the:
-GP2X/GP2XWiz ScummVM Forum: <http://forums.scummvm.org/viewforum.php?f=14>
-WiKi: <http://wiki.scummvm.org/index.php/GP2X>
+GP2X/GP2XWiz ScummVM Forum: <http://forums.scummvm.org/viewforum.php?f=14>
+WiKi: <http://wiki.scummvm.org/index.php/GP2X>
-for the most current information on the port and any updates to this
+for the most current information on the port and any updates to this
documentation.
+The wiki includes detailed instructions on how to use the port and
+control information.
+
------------------------------------------------------------------------
About the backend/port
-This is the readme for the offficial GP2X ScummVM backend (also known as
+This is the readme for the official GP2X ScummVM backend (also known as
the GP2X port).
This is an SVN test release of ScummVM for the GP2X, it would be
appreciated if this SVN test distribution was not mirrored and that
-people be directed to http://www.distant-earth.com/scummvm instead for
+people be directed to http://scummvm.distant-earth.com/ instead for
updated SVN builds.
Full supported official releases of the GP2X ScummVM backend are made in
line with main official releases and are avalalble from the ScummVM
downloads page <http://www.scummvm.org/downloads.php>.
-This build is in an active state of development and as such no
-?expected? behavior can be guaranteed ;).
-
-SVN builds are quickly tested with firmware 2.0.0 for reference.
-
-Please refer to the GP2X ScummVM forum
-<http://forums.scummvm.org/viewforum.php?f=14> and WiKi
-<http://wiki.scummvm.org/index.php/GP2X> for the latest information on
-the port.
-
-------------------------------------------------------------------------
-Game compatibility
-
-For information on the compatability of a specific game please refer to
-the GP2X compatability section of the ScummVM WiKi
-<http://wiki.scummvm.org/index.php/GP2X#Compatibility_List>.
-
-Please note the version and date of the ScummVM build you are running
-when reviewing the above list.
-
-------------------------------------------------------------------------
-Included engines
-
-Just because an engine is included does not mean any/all of its games
-are supported. Please check game compatability for more infomation.
-
------------------------------------------------------------------------
Supported audio options
@@ -79,106 +47,6 @@ FLAC audio is currently unsupported.
For best results use uncompressed audio in games.
------------------------------------------------------------------------
-Supported cut-scene options
-
-No cut scene compression options are currently supported.
-
-DXA video support will be added as soon as it is stable.
-
-------------------------------------------------------------------------
-Recent changes
-
-Refined audio hacks to reduce audio delay a little more.
-Enabled hardware scalar code.
-Now built using SDL 1.2.9 for the parts of the port that use SDL (some
-parts now hit the hardware directly).
-Enabled new launcher - (Ensure defaulttheme.zip is in the same folder as
-the executable).
-Aspect Ratio Correction can now be disabled ?per game?. When adding a
-game you can find this option on the GFX tab.
-Note: This will cause the game to run with a black border at the bottom
-as it will be rendered to a 320*200 frame.
-
-------------------------------------------------------------------------
-How to save
-
-NOTE: Everything is saved to the SD card, saves are stored in the saves
-folder under your main ScummVM executable unless you set another save
-location.
-
-The configiration file for ScummVM (.scummvmrc) is stored in the same
-place as the ScummVM executable.
-
-The save process below is for Scumm engine games but the principle is
-the same for all.
-
-In Game.
-
-1. Right Trigger
-2. Select SAVE with B
-3. Select a position with B
-4. Right trigger puts ? in the name box for some text.
-5. Press B to save
-
-Basically the emulated keys you can use are equivelent to the values
-buttons are mapped to,
-
-I have a virtual keyboard like the GP32 one (left/right on the stick to
-pick chars) to add in at some point ;-)
-
-------------------------------------------------------------------------
-Controller mappings
-
-Mouse emulation:
-
-Stick: Move Pointer
-Stick Click: ?light? Left Click
-B: Left click
-X: Right click
-
-Keyboard emulation:
-
-Start: Return
-Select: Escape
-Y: Space Bar (Pause)
-Right Trigger: Game Menu (Save, Load, Quit etc.)
-Volume Buttons: Increase and Decrease volume (5% per press)
-
-Fancy button combos:
-
-NOTE: To use button combos press and hold the Left Trigger then...
-
-Y: Toggle "zoom" mode - Try it in larger games like Broken Sword.
-Volume Buttons: Increase and Decrease subtitle speed (In SCUMM games)
-Right Trigger: 0 (For skipping the copy protection in Monkey Island 2)
-Select: Exit ScummVM completely (and gracefully)
-
-------------------------------------------------------------------------
-Know issues
-
-Possible random crash (well SegFault). I have had this happen twice and
-have not tracked down the cause.
-It happens very infrequently, both times it was in the DOTT CD intro.
-Saving often is never a bad idea anyhow.
-
-------------------------------------------------------------------------
-TODO's
-
-Fix save support when using the Sky engine (Beneath a Steel Sky) - You
-CAN'T save at the moment but auto save works.
-
-------------------------------------------------------------------------
-Additional resources/links
-
- * ScummVM WiKi GP2X page <http://wiki.scummvm.org/index.php/GP2X>
- * ScummVM forums GP2X forum
- <http://forums.scummvm.org/viewforum.php?f=14>
- * My own ScummVM page <http://scummvm.distant-earth.com/> (for
- SVN/test builds)
- * Main ScummVM site <http://www.scummvm.org> (for official supported
- release builds)
-
-------------------------------------------------------------------------
Credits
Core ScummVM code (c) The ScummVM Team
diff --git a/backends/platform/gp2x/build/config.sh b/backends/platform/gp2x/build/config.sh
index 2bc49564f7..e0a1bf1209 100755
--- a/backends/platform/gp2x/build/config.sh
+++ b/backends/platform/gp2x/build/config.sh
@@ -18,6 +18,7 @@ export DEFINES=-DNDEBUG
# Edit the configure line to suit.
cd ../../../..
./configure --backend=gp2x --disable-mt32emu --host=gp2x --disable-flac --disable-nasm --disable-hq-scalers --with-sdl-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6/bin --with-mpeg2-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-tremor --with-tremor-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-zlib --with-zlib-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-mad --with-mad-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-vkeybd
-#--enable-plugins --default-dynamic
+# --disable-release --enable-debug
+# --enable-plugins --default-dynamic
echo Generating config for GP2X complete. Check for errors.
diff --git a/backends/platform/gp2x/build/scummvm.gpe b/backends/platform/gp2x/build/scummvm.gpe
index 1e69c149b9..c6b051831c 100755
--- a/backends/platform/gp2x/build/scummvm.gpe
+++ b/backends/platform/gp2x/build/scummvm.gpe
@@ -4,7 +4,7 @@
mount -o sync,remount /dev/mmcsd/disc0/part1 /mnt/sd/
# Run ScummVM, important this bit.
-./scummvm.gp2x
+./scummvm.gph
# Sync the SD card to check that everything is written.
sync
diff --git a/backends/platform/gp2x/events.cpp b/backends/platform/gp2x/events.cpp
index a40967743c..08553968d2 100644
--- a/backends/platform/gp2x/events.cpp
+++ b/backends/platform/gp2x/events.cpp
@@ -30,7 +30,6 @@
#include "backends/platform/gp2x/gp2x-common.h"
#include "backends/platform/gp2x/gp2x-hw.h"
-#include "backends/keymapper/keymapper.h"
#include "common/util.h"
#include "common/events.h"
#include "graphics/scaler/aspect.h" // for aspect2Real
@@ -42,27 +41,46 @@
#define JOY_XAXIS 0
#define JOY_YAXIS 1
-/* GP2X Wiz: Main Joystick Mappings */
+/* Quick default button states for modifiers. */
+int BUTTON_STATE_L = false;
+
+enum {
+ /* DPAD/Stick */
+ BUTTON_UP = 0,
+ BUTTON_UPLEFT = 1,
+ BUTTON_LEFT = 2,
+ BUTTON_DOWNLEFT = 3,
+ BUTTON_DOWN = 4,
+ BUTTON_DOWNRIGHT = 5,
+ BUTTON_RIGHT = 6,
+ BUTTON_UPRIGHT = 7,
+ /* Joystick Buttons */
+ BUTTON_MENU = 8, // Start on F100 GP2X
+ BUTTON_SELECT = 9,
+ BUTTON_L = 10,
+ BUTTON_R = 11,
+ BUTTON_A = 12,
+ BUTTON_B = 13,
+ BUTTON_X = 14,
+ BUTTON_Y = 15,
+ BUTTON_VOLUP = 16,
+ BUTTON_VOLDOWN = 17,
+ BUTTON_CLICK = 18
+};
+
enum {
- GP2X_BUTTON_UP = 0,
- GP2X_BUTTON_UPLEFT = 1,
- GP2X_BUTTON_LEFT = 2,
- GP2X_BUTTON_DOWNLEFT = 3,
- GP2X_BUTTON_DOWN = 4,
- GP2X_BUTTON_DOWNRIGHT = 5,
- GP2X_BUTTON_RIGHT = 6,
- GP2X_BUTTON_UPRIGHT = 7,
- GP2X_BUTTON_START = 8,
- GP2X_BUTTON_SELECT = 9,
- GP2X_BUTTON_L = 10,
- GP2X_BUTTON_R = 11,
- GP2X_BUTTON_A = 12,
- GP2X_BUTTON_B = 13,
- GP2X_BUTTON_X = 14,
- GP2X_BUTTON_Y = 15,
- GP2X_BUTTON_VOLUP = 16,
- GP2X_BUTTON_VOLDOWN = 17,
- GP2X_BUTTON_CLICK = 18
+ /* Unused Joystick Buttons on the GP2X */
+ BUTTON_HOME = 51,
+ BUTTON_HOLD = 52,
+ BUTTON_HELP = 53,
+ BUTTON_HELP2 = 54
+};
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
};
static int mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
@@ -168,16 +186,22 @@ void OSystem_GP2X::handleKbdMouse() {
}
}
-static byte SDLModToOSystemKeyFlags(SDLMod mod) {
- byte b = 0;
+static void SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) {
+
+ event.kbd.flags = 0;
+
if (mod & KMOD_SHIFT)
- b |= Common::KBD_SHIFT;
+ event.kbd.flags |= Common::KBD_SHIFT;
if (mod & KMOD_ALT)
- b |= Common::KBD_ALT;
+ event.kbd.flags |= Common::KBD_ALT;
if (mod & KMOD_CTRL)
- b |= Common::KBD_CTRL;
+ event.kbd.flags |= Common::KBD_CTRL;
- return b;
+ // Sticky flags
+ if (mod & KMOD_NUM)
+ event.kbd.flags |= Common::KBD_NUM;
+ if (mod & KMOD_CAPS)
+ event.kbd.flags |= Common::KBD_CAPS;
}
void OSystem_GP2X::moveStick() {
@@ -205,7 +229,6 @@ void OSystem_GP2X::moveStick() {
_km.x_down_count = 0;
}
-
if ((stickBtn[0])||(stickBtn[1])||(stickBtn[7])){
if (_km.y_down_count!=2){
_km.y_vel = -1;
@@ -224,14 +247,9 @@ void OSystem_GP2X::moveStick() {
}
}
-/* Quick default button states for modifiers. */
-int GP2X_BUTTON_STATE_L = false;
-
bool OSystem_GP2X::pollEvent(Common::Event &event) {
SDL_Event ev;
ev.type = SDL_NOEVENT;
- int axis;
- byte b = 0;
handleKbdMouse();
@@ -242,398 +260,483 @@ bool OSystem_GP2X::pollEvent(Common::Event &event) {
return true;
}
- // GP2X Input mappings.
+ while (SDL_PollEvent(&ev)) {
+ preprocessEvents(&ev);
+ if (dispatchSDLEvent(ev, event))
+ return true;
+ }
+ return false;
+}
+
+bool OSystem_GP2X::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) {
+ switch (ev.type) {
+ case SDL_KEYDOWN:
+ return handleKeyDown(ev, event);
+ case SDL_KEYUP:
+ return handleKeyUp(ev, event);
+ case SDL_MOUSEMOTION:
+ return handleMouseMotion(ev, event);
+ case SDL_MOUSEBUTTONDOWN:
+ return handleMouseButtonDown(ev, event);
+ case SDL_MOUSEBUTTONUP:
+ return handleMouseButtonUp(ev, event);
+ case SDL_JOYBUTTONDOWN:
+ return handleJoyButtonDown(ev, event);
+ case SDL_JOYBUTTONUP:
+ return handleJoyButtonUp(ev, event);
+ case SDL_JOYAXISMOTION:
+ return handleJoyAxisMotion(ev, event);
+
+ case SDL_VIDEOEXPOSE:
+ _forceFull = true;
+ break;
+
+ case SDL_QUIT:
+ event.type = Common::EVENT_QUIT;
+ return true;
- /*
- Single Button
+ }
- Movement:
+ return false;
+}
- GP2X_BUTTON_UP Cursor Up
- GP2X_BUTTON_DOWN Cursor Down
- GP2X_BUTTON_LEFT Cursor Left
- GP2X_BUTTON_RIGHT Cursor Right
+bool OSystem_GP2X::handleKeyDown(SDL_Event &ev, Common::Event &event) {
- GP2X_BUTTON_UPLEFT Cursor Up Left
- GP2X_BUTTON_UPRIGHT Cursor Up Right
- GP2X_BUTTON_DOWNLEFT Cursor Down Left
- GP2X_BUTTON_DOWNRIGHT Cursor Down Right
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
- Button Emulation:
+ // Handle scroll lock as a key modifier
+ if (ev.key.keysym.sym == SDLK_SCROLLOCK)
+ _scrollLock = !_scrollLock;
- GP2X_BUTTON_CLICK Left Mouse Click
- GP2X_BUTTON_A . (Period)
- GP2X_BUTTON_B Left Mouse Click
- GP2X_BUTTON_Y Space Bar
- GP2X_BUTTON_X Right Mouse Click
- GP2X_BUTTON_L Combo Modifier (Left Trigger)
- GP2X_BUTTON_R Return (Right Trigger)
- GP2X_BUTTON_START F5 (Game Menu)
- GP2X_BUTTON_SELECT Escape
- GP2X_BUTTON_VOLUP /dev/mixer Global Volume Up
- GP2X_BUTTON_VOLDOWN /dev/mixer Global Volume Down
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
- Combos:
+ // Alt-Return and Alt-Enter toggle full screen mode
+ if (event.kbd.hasFlags(Common::KBD_ALT) && (ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_KP_ENTER)) {
+ beginGFXTransaction();
+ setFullscreenMode(!_videoMode.fullscreen);
+ endGFXTransaction();
+#ifdef USE_OSD
+ if (_videoMode.fullscreen)
+ displayMessageOnOSD("Fullscreen mode");
+ else
+ displayMessageOnOSD("Windowed mode");
+#endif
- GP2X_BUTTON_VOLUP & GP2X_BUTTON_VOLDOWN 0 (For Monkey 2 CP) or Virtual Keyboard if enabled
- GP2X_BUTTON_L & GP2X_BUTTON_SELECT Common::EVENT_QUIT (Calls Sync() to make sure SD is flushed)
- GP2X_BUTTON_L & GP2X_BUTTON_Y Toggles setZoomOnMouse() for larger then 320*240 games to scale to the point + raduis.
- GP2X_BUTTON_L & GP2X_BUTTON_START Common::EVENT_MAINMENU (ScummVM Global Main Menu)
- GP2X_BUTTON_L & GP2X_BUTTON_A Common::EVENT_PREDICTIVE_DIALOG for predictive text entry box (AGI games)
- */
+ return false;
+ }
- while (SDL_PollEvent(&ev)) {
+ // Alt-S: Create a screenshot
+ if (event.kbd.hasFlags(Common::KBD_ALT) && ev.key.keysym.sym == 's') {
+ char filename[20];
- switch (ev.type) {
- case SDL_KEYDOWN:{
- b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState());
+ for (int n = 0;; n++) {
+ SDL_RWops *file;
- const bool event_complete = remapKey(ev,event);
+ sprintf(filename, "scummvm%05d.bmp", n);
+ file = SDL_RWFromFile(filename, "r");
+ if (!file)
+ break;
+ SDL_RWclose(file);
+ }
+ if (saveScreenshot(filename))
+ printf("Saved '%s'\n", filename);
+ else
+ printf("Could not save screenshot!\n");
+ return false;
+ }
- if (event_complete)
- return true;
+ // Ctrl-m toggles mouse capture
+ if (event.kbd.hasFlags(Common::KBD_CTRL) && ev.key.keysym.sym == 'm') {
+ toggleMouseGrab();
+ return false;
+ }
- event.type = Common::EVENT_KEYDOWN;
- event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
- event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
+ if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'q') {
+ event.type = Common::EVENT_QUIT;
+ return true;
+ }
- return true;
- }
- case SDL_KEYUP:
- {
- const bool event_complete = remapKey(ev,event);
+ if ((ev.key.keysym.mod & KMOD_CTRL) && ev.key.keysym.sym == 'u') {
+ event.type = Common::EVENT_MUTE;
+ return true;
+ }
- if (event_complete)
- return true;
+ // Ctrl-Alt-<key> will change the GFX mode
+ if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) {
+ if (handleScalerHotkeys(ev.key))
+ return false;
+ }
- event.type = Common::EVENT_KEYUP;
- event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
- event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
- b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState());
+ if (remapKey(ev, event))
+ return true;
- // Ctrl-Alt-<key> will change the GFX mode
- if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) {
- // Swallow these key up events
- break;
- }
+ event.type = Common::EVENT_KEYDOWN;
+ event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
+ event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
- return true;
- }
- case SDL_MOUSEMOTION:
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, ev.motion.x, ev.motion.y);
+ return true;
+}
- setMousePos(event.mouse.x, event.mouse.y);
- return true;
+bool OSystem_GP2X::handleKeyUp(SDL_Event &ev, Common::Event &event) {
+ if (remapKey(ev, event))
+ return true;
+
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
+ event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
+
+ // Ctrl-Alt-<key> will change the GFX mode
+ SDLModToOSystemKeyFlags(SDL_GetModState(), event);
+
+ // Set the scroll lock sticky flag
+ if (_scrollLock)
+ event.kbd.flags |= Common::KBD_SCRL;
+
+ if (isScalerHotkey(event))
+ // Swallow these key up events
+ return false;
+
+ return true;
+}
+
+bool OSystem_GP2X::handleMouseMotion(SDL_Event &ev, Common::Event &event) {
+ event.type = Common::EVENT_MOUSEMOVE;
+ fillMouseEvent(event, ev.motion.x, ev.motion.y);
- case SDL_MOUSEBUTTONDOWN:
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONDOWN;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONDOWN;
+ setMousePos(event.mouse.x, event.mouse.y);
+ return true;
+}
+
+/* Custom handleMouseButtonDown/handleMouseButtonUp to deal with 'Tap Mode' for the touchscreen */
+
+bool OSystem_GP2X::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONDOWN; /* For normal mice etc. */
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONDOWN;
#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
- else if (ev.button.button == SDL_BUTTON_WHEELUP)
- event.type = Common::EVENT_WHEELUP;
- else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
- event.type = Common::EVENT_WHEELDOWN;
+ else if (ev.button.button == SDL_BUTTON_WHEELUP)
+ event.type = Common::EVENT_WHEELUP;
+ else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
+ event.type = Common::EVENT_WHEELDOWN;
#endif
#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONDOWN;
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONDOWN;
#endif
- else
- break;
+ else
+ return false;
- fillMouseEvent(event, ev.button.x, ev.button.y);
+ fillMouseEvent(event, ev.button.x, ev.button.y);
- return true;
+ return true;
+}
- case SDL_MOUSEBUTTONUP:
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONUP;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONUP;
+bool OSystem_GP2X::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONUP; /* For normal mice etc. */
+
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONUP;
#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONUP;
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONUP;
#endif
- else
- break;
- fillMouseEvent(event, ev.button.x, ev.button.y);
+ else
+ return false;
- return true;
+ fillMouseEvent(event, ev.button.x, ev.button.y);
+
+ return true;
+}
- // GP2X Button mapings. Main code
-
- case SDL_JOYBUTTONDOWN:
- _stickBtn[ev.jbutton.button] = 1;
- if (ev.jbutton.button == GP2X_BUTTON_B) {
- event.type = Common::EVENT_LBUTTONDOWN;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_CLICK) {
- event.type = Common::EVENT_LBUTTONDOWN;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_X) {
- event.type = Common::EVENT_RBUTTONDOWN;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (_stickBtn[GP2X_BUTTON_L] && (ev.jbutton.button == GP2X_BUTTON_SELECT)) {
- event.type = Common::EVENT_QUIT;
- } else if (ev.jbutton.button < 8) {
- moveStick();
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, _km.x, _km.y);
- } else {
- event.type = Common::EVENT_KEYDOWN;
- event.kbd.flags = 0;
- switch (ev.jbutton.button) {
- case GP2X_BUTTON_L:
- GP2X_BUTTON_STATE_L = true;
- break;
- case GP2X_BUTTON_R:
- if (GP2X_BUTTON_STATE_L == true) {
+/* Custom handleJoyButtonDown/handleJoyButtonUp to deal with the joystick buttons on GPH devices */
+
+bool OSystem_GP2X::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
+
+ _stickBtn[ev.jbutton.button] = 1;
+ event.kbd.flags = 0;
+
+ switch (ev.jbutton.button) {
+ case BUTTON_UP:
+ case BUTTON_UPLEFT:
+ case BUTTON_LEFT:
+ case BUTTON_DOWNLEFT:
+ case BUTTON_DOWN:
+ case BUTTON_DOWNRIGHT:
+ case BUTTON_RIGHT:
+ case BUTTON_UPRIGHT:
+ moveStick();
+ event.type = Common::EVENT_MOUSEMOVE;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_B:
+ case BUTTON_CLICK:
+ if (BUTTON_STATE_L == true) {
+ setZoomOnMouse();
+ fillMouseEvent(event, _km.x, _km.y);
+ } else {
+ event.type = Common::EVENT_LBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ }
+ break;
+ case BUTTON_X:
+ event.type = Common::EVENT_RBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_L:
+ BUTTON_STATE_L = true;
+ break;
+ case BUTTON_R:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
#ifdef ENABLE_VKEYBD
- event.kbd.keycode = Common::KEYCODE_F7;
- event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
+ event.kbd.keycode = Common::KEYCODE_F7;
+ event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
#else
- event.kbd.keycode = Common::KEYCODE_0;
- event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
+ event.kbd.keycode = Common::KEYCODE_0;
+ event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
#endif
- } else {
- event.kbd.keycode = Common::KEYCODE_RETURN;
- event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_SELECT:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_QUIT;
- } else {
- event.kbd.keycode = Common::KEYCODE_ESCAPE;
- event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_A:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_PREDICTIVE_DIALOG;
- } else {
- event.kbd.keycode = Common::KEYCODE_PERIOD;
- event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_Y:
- if (GP2X_BUTTON_STATE_L == true) {
- setZoomOnMouse();
- } else {
- event.kbd.keycode = Common::KEYCODE_SPACE;
- event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_START:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_MAINMENU;
- } else {
- event.kbd.keycode = Common::KEYCODE_F5;
- event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_VOLUP:
- GP2X_HW::mixerMoveVolume(2);
- if (GP2X_HW::volumeLevel == 100) {
- displayMessageOnOSD("Maximum Volume");
- } else {
- displayMessageOnOSD("Increasing Volume");
- }
- break;
-
- case GP2X_BUTTON_VOLDOWN:
- GP2X_HW::mixerMoveVolume(1);
- if (GP2X_HW::volumeLevel == 0) {
- displayMessageOnOSD("Minimal Volume");
- } else {
- displayMessageOnOSD("Decreasing Volume");
- }
- break;
- }
+ } else {
+ event.kbd.keycode = Common::KEYCODE_RETURN;
+ event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_SELECT:
+ case BUTTON_HOME:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_QUIT;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_A:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_PREDICTIVE_DIALOG;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_PERIOD;
+ event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_Y:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ GPH::ToggleTapMode();
+ if (GPH::tapmodeLevel == TAPMODE_LEFT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Left Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_RIGHT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Right Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_HOVER) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Hover (No Click)");
}
- return true;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_SPACE;
+ event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_MENU:
+ case BUTTON_HELP:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_MAINMENU;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_F5;
+ event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_VOLUP:
+ GP2X_HW::mixerMoveVolume(2);
+ if (GP2X_HW::volumeLevel == 100) {
+ displayMessageOnOSD("Maximum Volume");
+ } else {
+ displayMessageOnOSD("Increasing Volume");
+ }
+ break;
+
+ case BUTTON_VOLDOWN:
+ GP2X_HW::mixerMoveVolume(1);
+ if (GP2X_HW::volumeLevel == 0) {
+ displayMessageOnOSD("Minimal Volume");
+ } else {
+ displayMessageOnOSD("Decreasing Volume");
+ }
+ break;
+ case BUTTON_HOLD:
+ event.type = Common::EVENT_QUIT;
+ break;
+ case BUTTON_HELP2:
+ GPH::ToggleTapMode();
+ if (GPH::tapmodeLevel == TAPMODE_LEFT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Left Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_RIGHT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Right Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_HOVER) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Hover (No Click)");
+ }
+ break;
+ }
+ return true;
+}
- case SDL_JOYBUTTONUP:
- _stickBtn[ev.jbutton.button] = 0;
- if (ev.jbutton.button == GP2X_BUTTON_B) {
- event.type = Common::EVENT_LBUTTONUP;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_CLICK) {
- event.type = Common::EVENT_LBUTTONUP;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_X) {
- event.type = Common::EVENT_RBUTTONUP;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button < 8) {
- moveStick();
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, _km.x, _km.y);
- } else {
- event.type = Common::EVENT_KEYUP;
- event.kbd.flags = 0;
- switch (ev.jbutton.button) {
- case GP2X_BUTTON_SELECT:
- event.kbd.keycode = Common::KEYCODE_ESCAPE;
- event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_A:
- event.kbd.keycode = Common::KEYCODE_PERIOD;
- event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_Y:
- event.kbd.keycode = Common::KEYCODE_SPACE;
- event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_START:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_MAINMENU;
- } else {
- event.kbd.keycode = Common::KEYCODE_F5;
- event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_L:
- GP2X_BUTTON_STATE_L = false;
- break;
- case GP2X_BUTTON_R:
- if (GP2X_BUTTON_STATE_L == true) {
+bool OSystem_GP2X::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
+ _stickBtn[ev.jbutton.button] = 0;
+ event.kbd.flags = 0;
+
+ switch (ev.jbutton.button) {
+ case BUTTON_UP:
+ case BUTTON_UPLEFT:
+ case BUTTON_LEFT:
+ case BUTTON_DOWNLEFT:
+ case BUTTON_DOWN:
+ case BUTTON_DOWNRIGHT:
+ case BUTTON_RIGHT:
+ case BUTTON_UPRIGHT:
+ moveStick();
+ event.type = Common::EVENT_MOUSEMOVE;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_B:
+ case BUTTON_CLICK:
+ if (BUTTON_STATE_L == true) {
+ break;
+ } else {
+ event.type = Common::EVENT_LBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ }
+ break;
+ case BUTTON_X:
+ event.type = Common::EVENT_RBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_L:
+ BUTTON_STATE_L = false;
+ break;
+ case BUTTON_SELECT:
+ case BUTTON_HOME:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_A:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_PERIOD;
+ event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_Y:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_SPACE;
+ event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_MENU:
+ case BUTTON_HELP:
+ event.type = Common::EVENT_KEYUP;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_MAINMENU;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_F5;
+ event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_R:
+ event.type = Common::EVENT_KEYUP;
+ if (BUTTON_STATE_L == true) {
#ifdef ENABLE_VKEYBD
- event.kbd.keycode = Common::KEYCODE_F7;
- event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
+ event.kbd.keycode = Common::KEYCODE_F7;
+ event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
#else
- event.kbd.keycode = Common::KEYCODE_0;
- event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
+ event.kbd.keycode = Common::KEYCODE_0;
+ event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
#endif
- } else {
- event.kbd.keycode = Common::KEYCODE_RETURN;
- event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_VOLUP:
- break;
- case GP2X_BUTTON_VOLDOWN:
- break;
- }
- }
- return true;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_RETURN;
+ event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_VOLUP:
+ break;
+ case BUTTON_VOLDOWN:
+ break;
+ case BUTTON_HOLD:
+ break;
+ case BUTTON_HELP2:
+ break;
+ }
+return true;
+}
- case SDL_JOYAXISMOTION:
- axis = ev.jaxis.value;
- if ( axis > JOY_DEADZONE) {
- axis -= JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
- } else if ( axis < -JOY_DEADZONE ) {
- axis += JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
- } else
- axis = 0;
-
- if ( ev.jaxis.axis == JOY_XAXIS) {
- if (axis != 0) {
- _km.x_vel = (axis > 0) ? 1:-1;
- _km.x_down_count = 1;
- } else {
- _km.x_vel = 0;
- _km.x_down_count = 0;
- }
+bool OSystem_GP2X::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
+ int axis = ev.jaxis.value;
+ if ( axis > JOY_DEADZONE) {
+ axis -= JOY_DEADZONE;
+ event.type = Common::EVENT_MOUSEMOVE;
+ } else if ( axis < -JOY_DEADZONE ) {
+ axis += JOY_DEADZONE;
+ event.type = Common::EVENT_MOUSEMOVE;
+ } else
+ axis = 0;
+
+ if ( ev.jaxis.axis == JOY_XAXIS) {
+#ifdef JOY_ANALOG
+ _km.x_vel = axis/2000;
+ _km.x_down_count = 0;
+#else
+ if (axis != 0) {
+ _km.x_vel = (axis > 0) ? 1:-1;
+ _km.x_down_count = 1;
+ } else {
+ _km.x_vel = 0;
+ _km.x_down_count = 0;
+ }
+#endif
- } else if (ev.jaxis.axis == JOY_YAXIS) {
+ } else if (ev.jaxis.axis == JOY_YAXIS) {
#ifndef JOY_INVERT_Y
- axis = -axis;
+ axis = -axis;
#endif
#ifdef JOY_ANALOG
- _km.y_vel = -axis / 2000;
- _km.y_down_count = 0;
+ _km.y_vel = -axis / 2000;
+ _km.y_down_count = 0;
#else
- if (axis != 0) {
- _km.y_vel = (-axis > 0) ? 1: -1;
- _km.y_down_count = 1;
- } else {
- _km.y_vel = 0;
- _km.y_down_count = 0;
- }
-#endif
- }
-
- fillMouseEvent(event, _km.x, _km.y);
-
- return true;
-
- case SDL_VIDEOEXPOSE:
- _forceFull = true;
- break;
-
- case SDL_QUIT:
- event.type = Common::EVENT_QUIT;
- return true;
+ if (axis != 0) {
+ _km.y_vel = (-axis > 0) ? 1: -1;
+ _km.y_down_count = 1;
+ } else {
+ _km.y_vel = 0;
+ _km.y_down_count = 0;
}
+#endif
}
- return false;
-}
-
-bool OSystem_GP2X::remapKey(SDL_Event &ev,Common::Event &event) {
- return false;
-}
-
-void OSystem_GP2X::setupKeymapper() {
-#ifdef ENABLE_KEYMAPPER
- using namespace Common;
- Keymapper *mapper = getEventManager()->getKeymapper();
-
- HardwareKeySet *keySet = new HardwareKeySet();
- keySet->addHardwareKey(new HardwareKey( "a", KeyState(KEYCODE_a), "a", kActionKeyType ));
- keySet->addHardwareKey(new HardwareKey( "s", KeyState(KEYCODE_s), "s", kActionKeyType ));
- keySet->addHardwareKey(new HardwareKey( "d", KeyState(KEYCODE_d), "d", kActionKeyType ));
- keySet->addHardwareKey(new HardwareKey( "f", KeyState(KEYCODE_f), "f", kActionKeyType ));
- keySet->addHardwareKey(new HardwareKey( "n", KeyState(KEYCODE_n), "n (vk)", kTriggerLeftKeyType, kVirtualKeyboardActionType ));
- keySet->addHardwareKey(new HardwareKey( "m", KeyState(KEYCODE_m), "m (remap)", kTriggerRightKeyType, kKeyRemapActionType ));
- keySet->addHardwareKey(new HardwareKey( "[", KeyState(KEYCODE_LEFTBRACKET), "[ (select)", kSelectKeyType ));
- keySet->addHardwareKey(new HardwareKey( "]", KeyState(KEYCODE_RIGHTBRACKET), "] (start)", kStartKeyType ));
- mapper->registerHardwareKeySet(keySet);
-
- Keymap *globalMap = new Keymap("global");
- Keymap *guiMap = new Keymap("gui");
- Action *act;
- Event evt ;
-
- act = new Action(globalMap, "MENU", "Menu", kGenericActionType, kSelectKeyType);
- act->addKeyEvent(KeyState(KEYCODE_F5, ASCII_F5, 0));
-
- act = new Action(globalMap, "SKCT", "Skip", kGenericActionType, kActionKeyType);
- act->addKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE, 0));
-
- act = new Action(globalMap, "PAUS", "Pause", kGenericActionType, kStartKeyType);
- act->addKeyEvent(KeyState(KEYCODE_SPACE, ' ', 0));
-
- act = new Action(globalMap, "SKLI", "Skip line", kGenericActionType, kActionKeyType);
- act->addKeyEvent(KeyState(KEYCODE_PERIOD, '.', 0));
-
- act = new Action(globalMap, "VIRT", "Display keyboard", kVirtualKeyboardActionType);
- act->addKeyEvent(KeyState(KEYCODE_F6, ASCII_F6, 0));
- act = new Action(globalMap, "REMP", "Remap keys", kKeyRemapActionType);
- act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0));
+ fillMouseEvent(event, _km.x, _km.y);
- mapper->addGlobalKeymap(globalMap);
-
- act = new Action(guiMap, "CLOS", "Close", kGenericActionType, kStartKeyType);
- act->addKeyEvent(KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE, 0));
-
- act = new Action(guiMap, "CLIK", "Mouse click");
- act->addLeftClickEvent();
-
- act = new Action(guiMap, "VIRT", "Display keyboard", kVirtualKeyboardActionType);
- act->addKeyEvent(KeyState(KEYCODE_F6, ASCII_F6, 0));
-
- act = new Action(guiMap, "REMP", "Remap keys", kKeyRemapActionType);
- act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0));
+ return true;
+}
- mapper->addGlobalKeymap(guiMap);
- mapper->pushKeymap("global");
-#endif
+bool OSystem_GP2X::remapKey(SDL_Event &ev, Common::Event &event) {
+ return false;
}
-
diff --git a/backends/platform/gp2x/gp2x-bundle.mk b/backends/platform/gp2x/gp2x-bundle.mk
index c6fb72c1c3..67d22d1889 100755
--- a/backends/platform/gp2x/gp2x-bundle.mk
+++ b/backends/platform/gp2x/gp2x-bundle.mk
@@ -6,7 +6,7 @@ gp2x-bundle: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)"
$(MKDIR) "$(bundle_name)/saves"
$(MKDIR) "$(bundle_name)/engine-data"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/saves/PUT_SAVES_IN_THIS_DIR"
$(CP) $(srcdir)/backends/platform/gp2x/build/scummvm.gpe $(bundle_name)/
@@ -28,13 +28,13 @@ ifdef DYNAMIC_MODULES
endif
tar -C $(bundle_name) -cvjf $(bundle_name).tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
gp2x-bundle-debug: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)"
$(MKDIR) "$(bundle_name)/saves"
$(MKDIR) "$(bundle_name)/engine-data"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/saves/PUT_SAVES_IN_THIS_DIR"
$(CP) $(srcdir)/backends/platform/gp2x/build/scummvm.gpe $(bundle_name)/
@@ -46,15 +46,15 @@ gp2x-bundle-debug: $(EXECUTABLE)
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/
$(INSTALL) -c -m 644 $(DIST_FILES_ENGINEDATA) $(bundle_name)/engine-data
$(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip $(bundle_name)/
-
+
$(INSTALL) -c -m 777 $(srcdir)/$(EXECUTABLE) $(bundle_name)/$(EXECUTABLE)
ifdef DYNAMIC_MODULES
$(INSTALL) -d "$(bundle_name)/scummvm/plugins"
$(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
endif
-
+
tar -C $(bundle_name) -cvjf $(bundle_name)-debug.tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
.PHONY: gp2x-bundle gp2x-bundle-debug
diff --git a/backends/platform/gp2x/gp2x-common.h b/backends/platform/gp2x/gp2x-common.h
index b54e2d4d4f..1c8708a8b1 100644
--- a/backends/platform/gp2x/gp2x-common.h
+++ b/backends/platform/gp2x/gp2x-common.h
@@ -113,6 +113,24 @@ public:
// Sets up the keymapper with the backends hardware key set
void setupKeymapper();
+protected:
+ virtual bool dispatchSDLEvent(SDL_Event &ev, Common::Event &event);
+
+ // Handlers for specific SDL events, called by pollEvent.
+ // This way, if a backend inherits fromt the SDL backend, it can
+ // change the behavior of only a single event, without having to override all
+ // of pollEvent.
+ virtual bool handleKeyDown(SDL_Event &ev, Common::Event &event);
+ virtual bool handleKeyUp(SDL_Event &ev, Common::Event &event);
+ virtual bool handleMouseMotion(SDL_Event &ev, Common::Event &event);
+ virtual bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event);
+ virtual bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event);
+ virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event);
+ virtual bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event);
+ virtual bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event);
+
+public:
+
// Set function that generates samples
void setupMixer();
static void mixCallback(void *s, byte *samples, int len);
@@ -153,6 +171,7 @@ public:
bool hasFeature(Feature f);
void setFeatureState(Feature f, bool enable);
bool getFeatureState(Feature f);
+ void preprocessEvents(SDL_Event *event) {}
void displayMessageOnOSD(const char *msg);
@@ -297,6 +316,9 @@ protected:
kMouseColorKey = 1
};
+ // Scroll lock state - since SDL doesn't track it
+ bool _scrollLock;
+
// joystick
SDL_Joystick *_joystick;
bool _stickBtn[32];
@@ -374,7 +396,8 @@ protected:
virtual bool remapKey(SDL_Event &ev, Common::Event &event);
- void handleScalerHotkeys(const SDL_KeyboardEvent &key);
+ bool handleScalerHotkeys(const SDL_KeyboardEvent &key);
+ bool isScalerHotkey(const Common::Event &event);
void moveStick();
int _gp2xInputType;
diff --git a/backends/platform/gp2x/gp2x-hw.cpp b/backends/platform/gp2x/gp2x-hw.cpp
index 2dc5b4f579..6f544258ec 100644
--- a/backends/platform/gp2x/gp2x-hw.cpp
+++ b/backends/platform/gp2x/gp2x-hw.cpp
@@ -30,6 +30,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "gp2x-common.h"
#include "gp2x-hw.h"
@@ -197,3 +200,28 @@ void gp2x_video_wait_vsync(void)
} /* namespace GP2X_HW */
+namespace GPH {
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
+};
+
+int tapmodeLevel = TAPMODE_LEFT;
+
+void ToggleTapMode() {
+ if (tapmodeLevel == TAPMODE_LEFT) {
+ tapmodeLevel = TAPMODE_RIGHT;
+ } else if (tapmodeLevel == TAPMODE_RIGHT) {
+ tapmodeLevel = TAPMODE_HOVER;
+ } else if (tapmodeLevel == TAPMODE_HOVER) {
+ tapmodeLevel = TAPMODE_LEFT;
+ } else {
+ tapmodeLevel = TAPMODE_LEFT;
+ }
+}
+
+
+} /* namespace GPH */
diff --git a/backends/platform/gp2x/gp2x-hw.h b/backends/platform/gp2x/gp2x-hw.h
index 7e72812cc4..872c44f118 100644
--- a/backends/platform/gp2x/gp2x-hw.h
+++ b/backends/platform/gp2x/gp2x-hw.h
@@ -54,4 +54,12 @@ extern void gp2x_video_wait_vsync(void);
} /* namespace GP2X_HW */
+namespace GPH {
+
+extern int tapmodeLevel;
+
+extern void ToggleTapMode();
+
+} /* namespace GPH */
+
#endif //GP2X_HW_H
diff --git a/backends/platform/gp2x/gp2x-mem.cpp b/backends/platform/gp2x/gp2x-mem.cpp
index 97a34ffb6a..4a1802f311 100644
--- a/backends/platform/gp2x/gp2x-mem.cpp
+++ b/backends/platform/gp2x/gp2x-mem.cpp
@@ -28,6 +28,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
diff --git a/backends/platform/gp2x/gp2x.cpp b/backends/platform/gp2x/gp2x.cpp
index 88d4f9d632..0811380be9 100644
--- a/backends/platform/gp2x/gp2x.cpp
+++ b/backends/platform/gp2x/gp2x.cpp
@@ -28,6 +28,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "backends/platform/gp2x/gp2x-common.h"
#include "backends/platform/gp2x/gp2x-hw.h"
#include "backends/platform/gp2x/gp2x-mem.h"
diff --git a/backends/platform/gp2x/graphics.cpp b/backends/platform/gp2x/graphics.cpp
index 4a3c668c52..d776db218e 100644
--- a/backends/platform/gp2x/graphics.cpp
+++ b/backends/platform/gp2x/graphics.cpp
@@ -1601,7 +1601,7 @@ void OSystem_GP2X::displayMessageOnOSD(const char *msg) {
#pragma mark --- Misc ---
#pragma mark -
-void OSystem_GP2X::handleScalerHotkeys(const SDL_KeyboardEvent &key) {
+bool OSystem_GP2X::handleScalerHotkeys(const SDL_KeyboardEvent &key) {
// Ctrl-Alt-a toggles aspect ratio correction
if (key.keysym.sym == 'a') {
beginGFXTransaction();
@@ -1620,8 +1620,8 @@ void OSystem_GP2X::handleScalerHotkeys(const SDL_KeyboardEvent &key) {
);
displayMessageOnOSD(buffer);
-
- return;
+ internUpdateScreen();
+ return true;
}
int newMode = -1;
@@ -1641,7 +1641,7 @@ void OSystem_GP2X::handleScalerHotkeys(const SDL_KeyboardEvent &key) {
if (isNormalNumber || isKeypadNumber) {
_scalerType = key.keysym.sym - (isNormalNumber ? SDLK_1 : SDLK_KP1);
if (_scalerType >= ARRAYSIZE(s_gfxModeSwitchTable))
- return;
+ return false;
while (s_gfxModeSwitchTable[_scalerType][factor] < 0) {
assert(factor > 0);
@@ -1675,5 +1675,27 @@ void OSystem_GP2X::handleScalerHotkeys(const SDL_KeyboardEvent &key) {
displayMessageOnOSD(buffer);
}
}
+ internUpdateScreen();
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool OSystem_GP2X::isScalerHotkey(const Common::Event &event) {
+ if ((event.kbd.flags & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) {
+ const bool isNormalNumber = (Common::KEYCODE_1 <= event.kbd.keycode && event.kbd.keycode <= Common::KEYCODE_9);
+ const bool isKeypadNumber = (Common::KEYCODE_KP1 <= event.kbd.keycode && event.kbd.keycode <= Common::KEYCODE_KP9);
+ const bool isScaleKey = (event.kbd.keycode == Common::KEYCODE_EQUALS || event.kbd.keycode == Common::KEYCODE_PLUS || event.kbd.keycode == Common::KEYCODE_MINUS ||
+ event.kbd.keycode == Common::KEYCODE_KP_PLUS || event.kbd.keycode == Common::KEYCODE_KP_MINUS);
+
+ if (isNormalNumber || isKeypadNumber) {
+ int keyValue = event.kbd.keycode - (isNormalNumber ? Common::KEYCODE_1 : Common::KEYCODE_KP1);
+ if (keyValue >= ARRAYSIZE(s_gfxModeSwitchTable))
+ return false;
+ }
+ return (isScaleKey || event.kbd.keycode == 'a');
}
+ return false;
}
diff --git a/backends/platform/gp2xwiz/caanoo/config-alleng.sh b/backends/platform/gp2xwiz/caanoo/config-alleng.sh
deleted file mode 100755
index 7a097c268b..0000000000
--- a/backends/platform/gp2xwiz/caanoo/config-alleng.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-echo Quick script to make running configure all the time less painful
-echo and let all the build work be done from the backend/build folder.
-
-# Assume Caanoo toolchain/build env.
-. /opt/arm-caanoo/environment-setup
-
-# Export the tool names for cross-compiling
-export DEFINES=-DNDEBUG
-
-# Edit the configure line to suit.
-cd ../../../..
-./configure --backend=caanoo --disable-mt32emu --host=caanoo --disable-alsa --disable-flac --disable-nasm --disable-vorbis --disable-hq-scalers --with-sdl-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/bin --with-mpeg2-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr --enable-tremor --with-tremor-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr --enable-zlib --with-zlib-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr --enable-mad --with-mad-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr --enable-all-engines --enable-vkeybd --enable-plugins --default-dynamic
-
-echo Generating config for GP2X Caanoo complete. Check for errors.
diff --git a/backends/platform/gp2xwiz/caanoo/config.sh b/backends/platform/gp2xwiz/caanoo/config.sh
deleted file mode 100755
index 73ce5da624..0000000000
--- a/backends/platform/gp2xwiz/caanoo/config.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-echo Quick script to make running configure all the time less painful
-echo and let all the build work be done from the backend/build folder.
-
-# Assume Caanoo toolchain/build env.
-. /opt/arm-caanoo/environment-setup
-
-# Export the tool names for cross-compiling
-export DEFINES=-DNDEBUG
-
-# Edit the configure line to suit.
-cd ../../../..
-./configure --backend=caanoo --disable-mt32emu --host=caanoo --disable-alsa --disable-flac --disable-nasm --disable-vorbis --disable-hq-scalers --with-sdl-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr/bin --with-mpeg2-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr --enable-tremor --with-tremor-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr --enable-zlib --with-zlib-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr --enable-mad --with-mad-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr --enable-vkeybd --enable-plugins --default-dynamic
-
-echo Generating config for GP2X Caanoo complete. Check for errors.
diff --git a/backends/platform/gp2xwiz/gp2xwiz-events.cpp b/backends/platform/gp2xwiz/gp2xwiz-events.cpp
deleted file mode 100644
index 19ef24ef58..0000000000
--- a/backends/platform/gp2xwiz/gp2xwiz-events.cpp
+++ /dev/null
@@ -1,501 +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$
- *
- */
-
-/*
- * GP2X Wiz: Device Specific Event Handling.
- *
- */
-
-#include "backends/platform/gp2xwiz/gp2xwiz-sdl.h"
-#include "backends/platform/gp2xwiz/gp2xwiz-hw.h"
-#include "graphics/scaler/aspect.h"
-
-#include "common/util.h"
-#include "common/events.h"
-
-// FIXME move joystick defines out and replace with confile file options
-// we should really allow users to map any key to a joystick button using the keymapper.
-#define JOY_DEADZONE 2200
-
-#define JOY_XAXIS 0
-#define JOY_YAXIS 1
-
-/* GP2X Wiz: Main Joystick Mappings */
-enum {
- GP2X_BUTTON_UP = 0,
- GP2X_BUTTON_UPLEFT = 1,
- GP2X_BUTTON_LEFT = 2,
- GP2X_BUTTON_DOWNLEFT = 3,
- GP2X_BUTTON_DOWN = 4,
- GP2X_BUTTON_DOWNRIGHT = 5,
- GP2X_BUTTON_RIGHT = 6,
- GP2X_BUTTON_UPRIGHT = 7,
- GP2X_BUTTON_MENU = 8,
- GP2X_BUTTON_SELECT = 9,
- GP2X_BUTTON_L = 10,
- GP2X_BUTTON_R = 11,
- GP2X_BUTTON_A = 12,
- GP2X_BUTTON_B = 13,
- GP2X_BUTTON_X = 14,
- GP2X_BUTTON_Y = 15,
- GP2X_BUTTON_VOLUP = 16,
- GP2X_BUTTON_VOLDOWN = 17
-};
-
-static int mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
- if (key >= SDLK_F1 && key <= SDLK_F9) {
- return key - SDLK_F1 + Common::ASCII_F1;
- } else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
- return key - SDLK_KP0 + '0';
- } else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) {
- return key;
- } else if (unicode) {
- return unicode;
- } else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) {
- return key & ~0x20;
- } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) {
- return 0;
- }
- return key;
-}
-
-void OSystem_GP2XWIZ::fillMouseEvent(Common::Event &event, int x, int y) {
- if (_videoMode.mode == GFX_HALF && !_overlayVisible){
- event.mouse.x = x*2;
- event.mouse.y = y*2;
- } else {
- event.mouse.x = x;
- event.mouse.y = y;
- }
-
- // Update the "keyboard mouse" coords
- _km.x = x;
- _km.y = y;
-
- // Adjust for the screen scaling
- if (!_overlayVisible) {
- event.mouse.x /= _videoMode.scaleFactor;
- event.mouse.y /= _videoMode.scaleFactor;
- if (_videoMode.aspectRatioCorrection)
- event.mouse.y = aspect2Real(event.mouse.y);
- }
-}
-
-static byte SDLModToOSystemKeyFlags(SDLMod mod) {
- byte b = 0;
- if (mod & KMOD_SHIFT)
- b |= Common::KBD_SHIFT;
- if (mod & KMOD_ALT)
- b |= Common::KBD_ALT;
- if (mod & KMOD_CTRL)
- b |= Common::KBD_CTRL;
-
- return b;
-}
-
-void OSystem_GP2XWIZ::moveStick() {
- bool stickBtn[32];
-
- memcpy(stickBtn, _stickBtn, sizeof(stickBtn));
-
- if ((stickBtn[0])||(stickBtn[2])||(stickBtn[4])||(stickBtn[6]))
- stickBtn[1] = stickBtn[3] = stickBtn[5] = stickBtn[7] = 0;
-
- if ((stickBtn[1])||(stickBtn[2])||(stickBtn[3])) {
- if (_km.x_down_count!=2) {
- _km.x_vel = -1;
- _km.x_down_count = 1;
- } else
- _km.x_vel = -4;
- } else if ((stickBtn[5])||(stickBtn[6])||(stickBtn[7])) {
- if (_km.x_down_count!=2) {
- _km.x_vel = 1;
- _km.x_down_count = 1;
- } else
- _km.x_vel = 4;
- } else {
- _km.x_vel = 0;
- _km.x_down_count = 0;
- }
-
-
- if ((stickBtn[0])||(stickBtn[1])||(stickBtn[7])) {
- if (_km.y_down_count!=2) {
- _km.y_vel = -1;
- _km.y_down_count = 1;
- } else
- _km.y_vel = -4;
- } else if ((stickBtn[3])||(stickBtn[4])||(stickBtn[5])) {
- if (_km.y_down_count!=2) {
- _km.y_vel = 1;
- _km.y_down_count = 1;
- } else
- _km.y_vel = 4;
- } else {
- _km.y_vel = 0;
- _km.y_down_count = 0;
- }
-}
-
-/* Quick default button states for modifiers. */
-int GP2X_BUTTON_STATE_L = false;
-
-/* Override the SDL pollEvent with the Wiz's own event code. */
-bool OSystem_GP2XWIZ::pollEvent(Common::Event &event) {
- SDL_Event ev;
- ev.type = SDL_NOEVENT;
- int axis;
- byte b = 0;
-
- handleKbdMouse();
-
- // If the screen mode changed, send an Common::EVENT_SCREEN_CHANGED
- if (_modeChanged) {
- _modeChanged = false;
- event.type = Common::EVENT_SCREEN_CHANGED;
- return true;
- }
-
- // GP2X Wiz Input mappings.
-
- /*
- Single Button
-
- Movement:
-
- GP2X_BUTTON_UP Cursor Up
- GP2X_BUTTON_DOWN Cursor Down
- GP2X_BUTTON_LEFT Cursor Left
- GP2X_BUTTON_RIGHT Cursor Right
-
- GP2X_BUTTON_UPLEFT Cursor Up Left
- GP2X_BUTTON_UPRIGHT Cursor Up Right
- GP2X_BUTTON_DOWNLEFT Cursor Down Left
- GP2X_BUTTON_DOWNRIGHT Cursor Down Right
-
- Button Emulation:
-
- GP2X_BUTTON_A . (Period)
- GP2X_BUTTON_B Left Mouse Click
- GP2X_BUTTON_Y Space Bar
- GP2X_BUTTON_X Right Mouse Click
- GP2X_BUTTON_L Combo Modifier (Left Trigger)
- GP2X_BUTTON_R Return (Right Trigger)
- GP2X_BUTTON_MENU F5 (Game Menu)
- GP2X_BUTTON_SELECT Escape
- GP2X_BUTTON_VOLUP /dev/mixer Global Volume Up
- GP2X_BUTTON_VOLDOWN /dev/mixer Global Volume Down
-
- Combos:
-
- GP2X_BUTTON_VOLUP & GP2X_BUTTON_VOLDOWN 0 (For Monkey 2 CP) or Virtual Keyboard if enabled
- GP2X_BUTTON_L & GP2X_BUTTON_SELECT Common::EVENT_QUIT (Calls Sync() to make sure SD is flushed)
- GP2X_BUTTON_L & GP2X_BUTTON_MENU Common::EVENT_MAINMENU (ScummVM Global Main Menu)
- GP2X_BUTTON_L & GP2X_BUTTON_A Common::EVENT_PREDICTIVE_DIALOG for predictive text entry box (AGI games)
- */
-
- while (SDL_PollEvent(&ev)) {
-
- switch (ev.type) {
- case SDL_KEYDOWN:{
- b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState());
-
- const bool event_complete = remapKey(ev,event);
-
- if (event_complete)
- return true;
-
- event.type = Common::EVENT_KEYDOWN;
- event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
- event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
-
- return true;
- }
- case SDL_KEYUP:
- {
- const bool event_complete = remapKey(ev,event);
-
- if (event_complete)
- return true;
-
- event.type = Common::EVENT_KEYUP;
- event.kbd.keycode = (Common::KeyCode)ev.key.keysym.sym;
- event.kbd.ascii = mapKey(ev.key.keysym.sym, ev.key.keysym.mod, ev.key.keysym.unicode);
- b = event.kbd.flags = SDLModToOSystemKeyFlags(SDL_GetModState());
-
- // Ctrl-Alt-<key> will change the GFX mode
- if ((b & (Common::KBD_CTRL|Common::KBD_ALT)) == (Common::KBD_CTRL|Common::KBD_ALT)) {
- // Swallow these key up events
- break;
- }
-
- return true;
- }
- case SDL_MOUSEMOTION:
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, ev.motion.x, ev.motion.y);
-
- setMousePos(event.mouse.x, event.mouse.y);
- return true;
-
- case SDL_MOUSEBUTTONDOWN:
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONDOWN;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONDOWN;
-#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
- else if (ev.button.button == SDL_BUTTON_WHEELUP)
- event.type = Common::EVENT_WHEELUP;
- else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
- event.type = Common::EVENT_WHEELDOWN;
-#endif
-#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONDOWN;
-#endif
- else
- break;
-
- fillMouseEvent(event, ev.button.x, ev.button.y);
-
- return true;
-
- case SDL_MOUSEBUTTONUP:
- if (ev.button.button == SDL_BUTTON_LEFT)
- event.type = Common::EVENT_LBUTTONUP;
- else if (ev.button.button == SDL_BUTTON_RIGHT)
- event.type = Common::EVENT_RBUTTONUP;
-#if defined(SDL_BUTTON_MIDDLE)
- else if (ev.button.button == SDL_BUTTON_MIDDLE)
- event.type = Common::EVENT_MBUTTONUP;
-#endif
- else
- break;
- fillMouseEvent(event, ev.button.x, ev.button.y);
-
- return true;
-
- case SDL_JOYBUTTONDOWN:
- _stickBtn[ev.jbutton.button] = 1;
- if (ev.jbutton.button == GP2X_BUTTON_B) {
- event.type = Common::EVENT_LBUTTONDOWN;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_X) {
- event.type = Common::EVENT_RBUTTONDOWN;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (_stickBtn[GP2X_BUTTON_L] && (ev.jbutton.button == GP2X_BUTTON_SELECT)) {
- event.type = Common::EVENT_QUIT;
- } else if (ev.jbutton.button < 8) {
- moveStick();
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, _km.x, _km.y);
- } else {
- event.type = Common::EVENT_KEYDOWN;
- event.kbd.flags = 0;
- switch (ev.jbutton.button) {
- case GP2X_BUTTON_L:
- GP2X_BUTTON_STATE_L = true;
- break;
- case GP2X_BUTTON_R:
- if (GP2X_BUTTON_STATE_L == true) {
-#ifdef ENABLE_VKEYBD
- event.kbd.keycode = Common::KEYCODE_F7;
- event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
-#else
- event.kbd.keycode = Common::KEYCODE_0;
- event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
-#endif
- } else {
- event.kbd.keycode = Common::KEYCODE_RETURN;
- event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_SELECT:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_QUIT;
- } else {
- event.kbd.keycode = Common::KEYCODE_ESCAPE;
- event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_A:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_PREDICTIVE_DIALOG;
- } else {
- event.kbd.keycode = Common::KEYCODE_PERIOD;
- event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_Y:
- event.kbd.keycode = Common::KEYCODE_SPACE;
- event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_MENU:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_MAINMENU;
- } else {
- event.kbd.keycode = Common::KEYCODE_F5;
- event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_VOLUP:
- WIZ_HW::mixerMoveVolume(2);
- if (WIZ_HW::volumeLevel == 100) {
- displayMessageOnOSD("Maximum Volume");
- } else {
- displayMessageOnOSD("Increasing Volume");
- }
- break;
-
- case GP2X_BUTTON_VOLDOWN:
- WIZ_HW::mixerMoveVolume(1);
- if (WIZ_HW::volumeLevel == 0) {
- displayMessageOnOSD("Minimal Volume");
- } else {
- displayMessageOnOSD("Decreasing Volume");
- }
- break;
- }
- }
- return true;
-
- case SDL_JOYBUTTONUP:
- _stickBtn[ev.jbutton.button] = 0;
- if (ev.jbutton.button == GP2X_BUTTON_B) {
- event.type = Common::EVENT_LBUTTONUP;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button == GP2X_BUTTON_X) {
- event.type = Common::EVENT_RBUTTONUP;
- fillMouseEvent(event, _km.x, _km.y);
- } else if (ev.jbutton.button < 8) {
- moveStick();
- event.type = Common::EVENT_MOUSEMOVE;
- fillMouseEvent(event, _km.x, _km.y);
- } else {
- event.type = Common::EVENT_KEYUP;
- event.kbd.flags = 0;
- switch (ev.jbutton.button) {
- case GP2X_BUTTON_SELECT:
- event.kbd.keycode = Common::KEYCODE_ESCAPE;
- event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_A:
- event.kbd.keycode = Common::KEYCODE_PERIOD;
- event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_Y:
- event.kbd.keycode = Common::KEYCODE_SPACE;
- event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
- break;
- case GP2X_BUTTON_MENU:
- if (GP2X_BUTTON_STATE_L == true) {
- event.type = Common::EVENT_MAINMENU;
- } else {
- event.kbd.keycode = Common::KEYCODE_F5;
- event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_L:
- GP2X_BUTTON_STATE_L = false;
- break;
- case GP2X_BUTTON_R:
- if (GP2X_BUTTON_STATE_L == true) {
-#ifdef ENABLE_VKEYBD
- event.kbd.keycode = Common::KEYCODE_F7;
- event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
-#else
- event.kbd.keycode = Common::KEYCODE_0;
- event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
-#endif
- } else {
- event.kbd.keycode = Common::KEYCODE_RETURN;
- event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
- }
- break;
- case GP2X_BUTTON_VOLUP:
- break;
- case GP2X_BUTTON_VOLDOWN:
- break;
- }
- }
- return true;
-
- case SDL_JOYAXISMOTION:
- axis = ev.jaxis.value;
- if ( axis > JOY_DEADZONE) {
- axis -= JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
- } else if ( axis < -JOY_DEADZONE ) {
- axis += JOY_DEADZONE;
- event.type = Common::EVENT_MOUSEMOVE;
- } else
- axis = 0;
-
- if ( ev.jaxis.axis == JOY_XAXIS) {
- if (axis != 0) {
- _km.x_vel = (axis > 0) ? 1:-1;
- _km.x_down_count = 1;
- } else {
- _km.x_vel = 0;
- _km.x_down_count = 0;
- }
-
- } else if (ev.jaxis.axis == JOY_YAXIS) {
-#ifndef JOY_INVERT_Y
- axis = -axis;
-#endif
-#ifdef JOY_ANALOG
- _km.y_vel = -axis / 2000;
- _km.y_down_count = 0;
-#else
- if (axis != 0) {
- _km.y_vel = (-axis > 0) ? 1: -1;
- _km.y_down_count = 1;
- } else {
- _km.y_vel = 0;
- _km.y_down_count = 0;
- }
-#endif
- }
-
- fillMouseEvent(event, _km.x, _km.y);
-
- return true;
-
- case SDL_VIDEOEXPOSE:
- _forceFull = true;
- break;
-
- case SDL_QUIT:
- event.type = Common::EVENT_QUIT;
- return true;
- }
- }
- return false;
-}
-
-bool OSystem_GP2XWIZ::remapKey(SDL_Event &ev,Common::Event &event) {
- return false;
-}
diff --git a/backends/platform/gp2xwiz/build/README-GP2XWIZ b/backends/platform/gph/build/README-GPH
index ec8142a6f3..269fa901c9 100644
--- a/backends/platform/gp2xwiz/build/README-GP2XWIZ
+++ b/backends/platform/gph/build/README-GPH
@@ -19,10 +19,10 @@ Contents:
Please refer to the:
-GP2X/GP2XWiz ScummVM Forum: <http://forums.scummvm.org/viewforum.php?f=14>
-WiKi: <http://wiki.scummvm.org/index.php/GP2XWiz>
+GP2X/GP2XWiz ScummVM Forum: <http://forums.scummvm.org/viewforum.php?f=14>
+WiKi: <http://wiki.scummvm.org/index.php/GP2XWiz>
-for the most current information on the port and any updates to this
+for the most current information on the port and any updates to this
documentation.
------------------------------------------------------------------------
@@ -112,7 +112,7 @@ Fancy button combos:
NOTE: To use button combos press and hold the Left Trigger then...
-Right Trigger: Display Virtual Keyboard
+Right Trigger: Display Virtual Keyboard
Menu: Bring up the Global main menu for ScummVM
Select: Exit ScummVM completely (and gracefully)
diff --git a/backends/platform/gp2xwiz/build/build.sh b/backends/platform/gph/build/build.sh
index 876c3e378a..876c3e378a 100755
--- a/backends/platform/gp2xwiz/build/build.sh
+++ b/backends/platform/gph/build/build.sh
diff --git a/backends/platform/gp2xwiz/build/bundle-debug.sh b/backends/platform/gph/build/bundle-debug.sh
index cd5145b31d..cd5145b31d 100755
--- a/backends/platform/gp2xwiz/build/bundle-debug.sh
+++ b/backends/platform/gph/build/bundle-debug.sh
diff --git a/backends/platform/gp2xwiz/build/bundle.sh b/backends/platform/gph/build/bundle.sh
index 579e2dc77b..579e2dc77b 100755
--- a/backends/platform/gp2xwiz/build/bundle.sh
+++ b/backends/platform/gph/build/bundle.sh
diff --git a/backends/platform/gp2xwiz/build/clean.sh b/backends/platform/gph/build/clean.sh
index 5ec1b9e62c..5ec1b9e62c 100755
--- a/backends/platform/gp2xwiz/build/clean.sh
+++ b/backends/platform/gph/build/clean.sh
diff --git a/backends/platform/gp2xwiz/build/config-alleng.sh b/backends/platform/gph/build/config-alleng.sh
index cfed463edf..9ec8a09cd2 100755
--- a/backends/platform/gp2xwiz/build/config-alleng.sh
+++ b/backends/platform/gph/build/config-alleng.sh
@@ -13,10 +13,15 @@ export CXX=arm-open2x-linux-g++
export CXXFLAGS="-mcpu=arm926ej-s -mtune=arm926ej-s"
export CPPFLAGS=-I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include
export LDFLAGS=-L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib
-export DEFINES=-DNDEBUG
# Edit the configure line to suit.
cd ../../../..
-./configure --backend=gp2xwiz --disable-mt32emu --host=gp2xwiz --disable-flac --disable-nasm --disable-hq-scalers --with-sdl-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6/bin --with-mpeg2-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-tremor --with-tremor-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-zlib --with-zlib-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-mad --with-mad-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-all-engines --enable-vkeybd --enable-plugins --default-dynamic
+./configure --backend=gph --disable-mt32emu --host=gp2xwiz --disable-flac --disable-nasm --disable-hq-scalers \
+ --with-sdl-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6/bin --with-mpeg2-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-tremor --with-tremor-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-zlib --with-zlib-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-mad --with-mad-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-png --with-png-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-vkeybd --enable-all-engines --enable-plugins --default-dynamic
echo Generating config for GP2X Wiz complete. Check for errors.
diff --git a/backends/platform/gp2xwiz/build/config.sh b/backends/platform/gph/build/config.sh
index 54c4795298..ac7c34ad12 100755
--- a/backends/platform/gp2xwiz/build/config.sh
+++ b/backends/platform/gph/build/config.sh
@@ -13,10 +13,15 @@ export CXX=arm-open2x-linux-g++
export CXXFLAGS="-mcpu=arm926ej-s -mtune=arm926ej-s"
export CPPFLAGS=-I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include
export LDFLAGS=-L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib
-export DEFINES=-DNDEBUG
# Edit the configure line to suit.
cd ../../../..
-./configure --backend=gp2xwiz --disable-mt32emu --host=gp2xwiz --disable-flac --disable-nasm --disable-hq-scalers --with-sdl-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6/bin --with-mpeg2-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-tremor --with-tremor-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-zlib --with-zlib-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-mad --with-mad-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 --enable-vkeybd --enable-plugins --default-dynamic
+./configure --backend=gph --disable-mt32emu --host=gp2xwiz --disable-flac --disable-nasm --disable-hq-scalers \
+ --with-sdl-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6/bin --with-mpeg2-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-tremor --with-tremor-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-zlib --with-zlib-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-mad --with-mad-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-png --with-png-prefix=/opt/open2x/gcc-4.1.1-glibc-2.3.6 \
+ --enable-vkeybd --enable-plugins --default-dynamic
echo Generating config for GP2X Wiz complete. Check for errors.
diff --git a/backends/platform/gp2xwiz/build/scummvm-gdb.gpe b/backends/platform/gph/build/scummvm-gdb.gpe
index 64b6c8b974..63ce193ca8 100755
--- a/backends/platform/gp2xwiz/build/scummvm-gdb.gpe
+++ b/backends/platform/gph/build/scummvm-gdb.gpe
@@ -6,7 +6,7 @@ export LD_LIBRARY_PATH=`pwd`/lib:$LD_LIBRARY_PATH
# Run ScummVM via GDB (so make sure you have a terminal open or serial).
# Oh, and GDB installed of course ;)
-gdb --args ./scummvm.wiz --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
+gdb --args ./scummvm.gph --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
# Sync the SD card to check that everything is written.
sync
diff --git a/backends/platform/gp2xwiz/build/scummvm.gpe b/backends/platform/gph/build/scummvm.gpe
index 42cc00a22a..59ff562aeb 100755
--- a/backends/platform/gp2xwiz/build/scummvm.gpe
+++ b/backends/platform/gph/build/scummvm.gpe
@@ -5,7 +5,7 @@
export LD_LIBRARY_PATH=`pwd`/lib:$LD_LIBRARY_PATH
# Run ScummVM, important this bit.
-./scummvm.wiz --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
+./scummvm.gph --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
# Sync the SD card to check that everything is written.
sync
diff --git a/backends/platform/gp2xwiz/build/scummvm.ini b/backends/platform/gph/build/scummvm.ini
index 5a8e6eefa1..c9cce92379 100644
--- a/backends/platform/gp2xwiz/build/scummvm.ini
+++ b/backends/platform/gph/build/scummvm.ini
@@ -2,3 +2,4 @@
name="ScummVM"
path="/scummvm/scummvm.gpe"
icon="/scummvm/scummvm.png"
+title="/scummvm/scummvmb.png"
diff --git a/backends/platform/gp2xwiz/build/scummvm.png b/backends/platform/gph/build/scummvm.png
index 128e59efc4..128e59efc4 100644
--- a/backends/platform/gp2xwiz/build/scummvm.png
+++ b/backends/platform/gph/build/scummvm.png
Binary files differ
diff --git a/backends/platform/gph/build/scummvmb.png b/backends/platform/gph/build/scummvmb.png
new file mode 100644
index 0000000000..24a27bc0e1
--- /dev/null
+++ b/backends/platform/gph/build/scummvmb.png
Binary files differ
diff --git a/backends/platform/gp2xwiz/caanoo/caanoo-bundle.mk b/backends/platform/gph/caanoo-bundle.mk
index f8ac26382d..b7b3c9e188 100755
--- a/backends/platform/gp2xwiz/caanoo/caanoo-bundle.mk
+++ b/backends/platform/gph/caanoo-bundle.mk
@@ -11,13 +11,14 @@ caanoo-bundle: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)/scummvm/saves"
$(MKDIR) "$(bundle_name)/scummvm/engine-data"
$(MKDIR) "$(bundle_name)/scummvm/lib"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/scummvm/saves/PUT_SAVES_IN_THIS_DIR"
- $(CP) $(srcdir)/backends/platform/gp2xwiz/caanoo/scummvm.gpe $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.png $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/README-GP2XWIZ $(bundle_name)/scummvm/README-CAANOO
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.ini $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/gph/caanoo/scummvm.gpe $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvmb.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/README-GPH $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.ini $(bundle_name)/
$(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/
@@ -31,9 +32,9 @@ ifdef DYNAMIC_MODULES
$(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
$(STRIP) $(bundle_name)/scummvm/plugins/*
endif
-
+
tar -C $(bundle_name) -cvjf $(bundle_name).tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
caanoo-bundle-debug: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)"
@@ -41,13 +42,14 @@ caanoo-bundle-debug: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)/scummvm/saves"
$(MKDIR) "$(bundle_name)/scummvm/engine-data"
$(MKDIR) "$(bundle_name)/scummvm/lib"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/scummvm/saves/PUT_SAVES_IN_THIS_DIR"
- $(CP) $(srcdir)/backends/platform/gp2xwiz/caanoo/scummvm-gdb.gpe $(bundle_name)/scummvm/scummvm.gpe
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.png $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/README-GP2XWIZ $(bundle_name)/scummvm/README-CAANOO
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.ini $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/gph/caanoo/scummvm-gdb.gpe $(bundle_name)/scummvm/scummvm.gpe
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvmb.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/README-GPH $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.ini $(bundle_name)/
$(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/
@@ -62,6 +64,6 @@ ifdef DYNAMIC_MODULES
endif
tar -C $(bundle_name) -cvjf $(bundle_name)-debug.tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
.PHONY: caanoo-bundle caanoo-bundle-debug
diff --git a/backends/platform/gp2xwiz/caanoo/build.sh b/backends/platform/gph/caanoo/build.sh
index 8000d2595d..8000d2595d 100755
--- a/backends/platform/gp2xwiz/caanoo/build.sh
+++ b/backends/platform/gph/caanoo/build.sh
diff --git a/backends/platform/gp2xwiz/caanoo/bundle-debug.sh b/backends/platform/gph/caanoo/bundle-debug.sh
index 2d5cefe80e..2d5cefe80e 100755
--- a/backends/platform/gp2xwiz/caanoo/bundle-debug.sh
+++ b/backends/platform/gph/caanoo/bundle-debug.sh
diff --git a/backends/platform/gp2xwiz/caanoo/bundle.sh b/backends/platform/gph/caanoo/bundle.sh
index 76fd31cec6..76fd31cec6 100755
--- a/backends/platform/gp2xwiz/caanoo/bundle.sh
+++ b/backends/platform/gph/caanoo/bundle.sh
diff --git a/backends/platform/gp2xwiz/caanoo/clean.sh b/backends/platform/gph/caanoo/clean.sh
index 5ec1b9e62c..5ec1b9e62c 100755
--- a/backends/platform/gp2xwiz/caanoo/clean.sh
+++ b/backends/platform/gph/caanoo/clean.sh
diff --git a/backends/platform/gph/caanoo/config-alleng.sh b/backends/platform/gph/caanoo/config-alleng.sh
new file mode 100755
index 0000000000..97fed942fa
--- /dev/null
+++ b/backends/platform/gph/caanoo/config-alleng.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+echo Quick script to make running configure all the time less painful
+echo and let all the build work be done from the backend/build folder.
+
+# Assume Caanoo toolchain/build env and source it.
+. /opt/arm-caanoo/environment-setup
+
+# Edit the configure line to suit.
+cd ../../../..
+./configure --backend=caanoo --disable-mt32emu --host=caanoo --disable-alsa --disable-flac \
+ --disable-nasm --disable-vorbis --disable-hq-scalers \
+ --with-sdl-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr/bin \
+ --with-mpeg2-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-tremor --with-tremor-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-zlib --with-zlib-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-mad --with-mad-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-png --with-png-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-all-engines --enable-vkeybd --enable-plugins --default-dynamic
+
+echo Generating config for Caanoo complete. Check for errors.
diff --git a/backends/platform/gph/caanoo/config.sh b/backends/platform/gph/caanoo/config.sh
new file mode 100755
index 0000000000..11d597481a
--- /dev/null
+++ b/backends/platform/gph/caanoo/config.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+echo Quick script to make running configure all the time less painful
+echo and let all the build work be done from the backend/build folder.
+
+# Assume Caanoo toolchain/build env.
+. /opt/arm-caanoo/environment-setup
+
+# Edit the configure line to suit.
+cd ../../../..
+./configure --backend=caanoo --disable-mt32emu --host=caanoo --disable-alsa --disable-flac \
+ --disable-nasm --disable-vorbis --disable-hq-scalers \
+ --with-sdl-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr/bin \
+ --with-mpeg2-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-tremor --with-tremor-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-zlib --with-zlib-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-mad --with-mad-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-png --with-png-prefix=/opt/arm-caanoo/arm-none-linux-gnueabi/usr \
+ --enable-vkeybd --enable-plugins --default-dynamic
+
+echo Generating config for GP2X Caanoo complete. Check for errors.
diff --git a/backends/platform/gp2xwiz/caanoo/scummvm-gdb.gpe b/backends/platform/gph/caanoo/scummvm-gdb.gpe
index a8f2aae0fe..63ce193ca8 100755
--- a/backends/platform/gp2xwiz/caanoo/scummvm-gdb.gpe
+++ b/backends/platform/gph/caanoo/scummvm-gdb.gpe
@@ -6,7 +6,7 @@ export LD_LIBRARY_PATH=`pwd`/lib:$LD_LIBRARY_PATH
# Run ScummVM via GDB (so make sure you have a terminal open or serial).
# Oh, and GDB installed of course ;)
-gdb --args ./scummvm.caanoo --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
+gdb --args ./scummvm.gph --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
# Sync the SD card to check that everything is written.
sync
diff --git a/backends/platform/gp2xwiz/caanoo/scummvm.gpe b/backends/platform/gph/caanoo/scummvm.gpe
index 8341ffc3d7..37d0f65d18 100755
--- a/backends/platform/gp2xwiz/caanoo/scummvm.gpe
+++ b/backends/platform/gph/caanoo/scummvm.gpe
@@ -5,7 +5,7 @@
export LD_LIBRARY_PATH=`pwd`/lib:$LD_LIBRARY_PATH
# Run ScummVM, important this bit.
-./scummvm.caanoo --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
+./scummvm.gph --fullscreen --gfx-mode=1x --config=$(pwd)/.scummvmrc
# Sync the SD card to check that everything is written.
sync
diff --git a/backends/platform/gp2xwiz/gp2xwiz-bundle.mk b/backends/platform/gph/gp2xwiz-bundle.mk
index fa5a247865..df4cae7f4f 100755
--- a/backends/platform/gp2xwiz/gp2xwiz-bundle.mk
+++ b/backends/platform/gph/gp2xwiz-bundle.mk
@@ -11,13 +11,14 @@ gp2xwiz-bundle: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)/scummvm/saves"
$(MKDIR) "$(bundle_name)/scummvm/engine-data"
$(MKDIR) "$(bundle_name)/scummvm/lib"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/scummvm/saves/PUT_SAVES_IN_THIS_DIR"
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.gpe $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.png $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/README-GP2XWIZ $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.ini $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.gpe $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvmb.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/README-GPH $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.ini $(bundle_name)/
$(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/
@@ -31,12 +32,12 @@ ifdef DYNAMIC_MODULES
$(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
$(STRIP) $(bundle_name)/scummvm/plugins/*
endif
-
+
$(CP) $(libloc)/../lib/libz.so.1.2.3 $(bundle_name)/scummvm/lib/libz.so.1
$(CP) $(libloc)/../lib/libvorbisidec.so.1.0.2 $(bundle_name)/scummvm/lib/libvorbisidec.so.1
tar -C $(bundle_name) -cvjf $(bundle_name).tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
gp2xwiz-bundle-debug: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)"
@@ -44,13 +45,14 @@ gp2xwiz-bundle-debug: $(EXECUTABLE)
$(MKDIR) "$(bundle_name)/scummvm/saves"
$(MKDIR) "$(bundle_name)/scummvm/engine-data"
$(MKDIR) "$(bundle_name)/scummvm/lib"
-
+
echo "Please put your save games in this dir" >> "$(bundle_name)/scummvm/saves/PUT_SAVES_IN_THIS_DIR"
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm-gdb.gpe $(bundle_name)/scummvm/scummvm.gpe
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.png $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/README-GP2XWIZ $(bundle_name)/scummvm/
- $(CP) $(srcdir)/backends/platform/gp2xwiz/build/scummvm.ini $(bundle_name)/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm-gdb.gpe $(bundle_name)/scummvm/scummvm.gpe
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvmb.png $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/README-GPH $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/gph/build/scummvm.ini $(bundle_name)/
$(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/
@@ -63,11 +65,11 @@ ifdef DYNAMIC_MODULES
$(INSTALL) -d "$(bundle_name)/scummvm/plugins"
$(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
endif
-
+
$(CP) $(libloc)/../lib/libz.so.1.2.3 $(bundle_name)/scummvm/lib/libz.so.1
$(CP) $(libloc)/../lib/libvorbisidec.so.1.0.2 $(bundle_name)/scummvm/lib/libvorbisidec.so.1
tar -C $(bundle_name) -cvjf $(bundle_name)-debug.tar.bz2 .
- rm -R ./$(bundle_name)
+ rm -R ./$(bundle_name)
.PHONY: gp2xwiz-bundle gp2xwiz-bundle-debug
diff --git a/backends/platform/gph/gph-events.cpp b/backends/platform/gph/gph-events.cpp
new file mode 100644
index 0000000000..2a6237c794
--- /dev/null
+++ b/backends/platform/gph/gph-events.cpp
@@ -0,0 +1,481 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * GPH: Device Specific Event Handling.
+ *
+ */
+
+#include "backends/platform/gph/gph-sdl.h"
+#include "backends/platform/gph/gph-hw.h"
+#include "graphics/scaler/aspect.h"
+
+#include "common/util.h"
+#include "common/events.h"
+
+#define JOY_DEADZONE 2200
+
+#define JOY_XAXIS 0
+#define JOY_YAXIS 1
+
+/* Quick default button states for modifiers. */
+int BUTTON_STATE_L = false;
+
+#if defined(CAANOO)
+
+ /* Caanoo: Main Joystick Button Mappings */
+ /* The Caanoo has an analogue stick so no digital DPAD */
+ enum {
+ /* Joystick Buttons */
+ BUTTON_A = 0,
+ BUTTON_X = 1,
+ BUTTON_B = 2,
+ BUTTON_Y = 3,
+ BUTTON_L = 4,
+ BUTTON_R = 5,
+ BUTTON_HOME = 6, // Home
+ BUTTON_HOLD = 7, // Hold (on Power)
+ BUTTON_HELP = 8, // Help I
+ BUTTON_HELP2 = 9, // Help II
+ BUTTON_CLICK = 10 // Stick Click
+ };
+
+ enum {
+ /* Unused Joystick Buttons on the Caanoo */
+ BUTTON_VOLUP = 51,
+ BUTTON_VOLDOWN = 52,
+ BUTTON_UP = 53,
+ BUTTON_UPLEFT = 54,
+ BUTTON_LEFT = 55,
+ BUTTON_DOWNLEFT = 56,
+ BUTTON_DOWN = 57,
+ BUTTON_DOWNRIGHT = 58,
+ BUTTON_RIGHT = 59,
+ BUTTON_UPRIGHT = 60,
+ BUTTON_MENU = 61,
+ BUTTON_SELECT = 62
+ };
+
+#else
+
+ /* Wiz: Main Joystick Mappings */
+ enum {
+ /* DPAD */
+ BUTTON_UP = 0,
+ BUTTON_UPLEFT = 1,
+ BUTTON_LEFT = 2,
+ BUTTON_DOWNLEFT = 3,
+ BUTTON_DOWN = 4,
+ BUTTON_DOWNRIGHT = 5,
+ BUTTON_RIGHT = 6,
+ BUTTON_UPRIGHT = 7,
+ /* Joystick Buttons */
+ BUTTON_MENU = 8,
+ BUTTON_SELECT = 9,
+ BUTTON_L = 10,
+ BUTTON_R = 11,
+ BUTTON_A = 12,
+ BUTTON_B = 13,
+ BUTTON_X = 14,
+ BUTTON_Y = 15,
+ BUTTON_VOLUP = 16,
+ BUTTON_VOLDOWN = 17
+ };
+
+ enum {
+ /* Unused Joystick Buttons on the Wiz */
+ BUTTON_HOME = 51,
+ BUTTON_HOLD = 52,
+ BUTTON_CLICK = 53,
+ BUTTON_HELP = 54,
+ BUTTON_HELP2 = 55
+ };
+
+#endif
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
+};
+
+static int mapKey(SDLKey key, SDLMod mod, Uint16 unicode) {
+ if (key >= SDLK_F1 && key <= SDLK_F9) {
+ return key - SDLK_F1 + Common::ASCII_F1;
+ } else if (key >= SDLK_KP0 && key <= SDLK_KP9) {
+ return key - SDLK_KP0 + '0';
+ } else if (key >= SDLK_UP && key <= SDLK_PAGEDOWN) {
+ return key;
+ } else if (unicode) {
+ return unicode;
+ } else if (key >= 'a' && key <= 'z' && (mod & KMOD_SHIFT)) {
+ return key & ~0x20;
+ } else if (key >= SDLK_NUMLOCK && key <= SDLK_EURO) {
+ return 0;
+ }
+ return key;
+}
+
+
+void OSystem_GPH::fillMouseEvent(Common::Event &event, int x, int y) {
+ if (_videoMode.mode == GFX_HALF && !_overlayVisible){
+ event.mouse.x = x*2;
+ event.mouse.y = y*2;
+ } else {
+ event.mouse.x = x;
+ event.mouse.y = y;
+ }
+
+ // Update the "keyboard mouse" coords
+ _km.x = x;
+ _km.y = y;
+
+ // Adjust for the screen scaling
+ if (!_overlayVisible) {
+ event.mouse.x /= _videoMode.scaleFactor;
+ event.mouse.y /= _videoMode.scaleFactor;
+ if (_videoMode.aspectRatioCorrection)
+ event.mouse.y = aspect2Real(event.mouse.y);
+ }
+}
+
+
+void OSystem_GPH::moveStick() {
+ bool stickBtn[32];
+
+ memcpy(stickBtn, _stickBtn, sizeof(stickBtn));
+
+ if ((stickBtn[0])||(stickBtn[2])||(stickBtn[4])||(stickBtn[6]))
+ stickBtn[1] = stickBtn[3] = stickBtn[5] = stickBtn[7] = 0;
+
+ if ((stickBtn[1])||(stickBtn[2])||(stickBtn[3])) {
+ if (_km.x_down_count!=2) {
+ _km.x_vel = -1;
+ _km.x_down_count = 1;
+ } else
+ _km.x_vel = -4;
+ } else if ((stickBtn[5])||(stickBtn[6])||(stickBtn[7])) {
+ if (_km.x_down_count!=2) {
+ _km.x_vel = 1;
+ _km.x_down_count = 1;
+ } else
+ _km.x_vel = 4;
+ } else {
+ _km.x_vel = 0;
+ _km.x_down_count = 0;
+ }
+
+ if ((stickBtn[0])||(stickBtn[1])||(stickBtn[7])) {
+ if (_km.y_down_count!=2) {
+ _km.y_vel = -1;
+ _km.y_down_count = 1;
+ } else
+ _km.y_vel = -4;
+ } else if ((stickBtn[3])||(stickBtn[4])||(stickBtn[5])) {
+ if (_km.y_down_count!=2) {
+ _km.y_vel = 1;
+ _km.y_down_count = 1;
+ } else
+ _km.y_vel = 4;
+ } else {
+ _km.y_vel = 0;
+ _km.y_down_count = 0;
+ }
+}
+
+/* Custom handleMouseButtonDown/handleMouseButtonUp to deal with 'Tap Mode' for the touchscreen */
+
+bool OSystem_GPH::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (GPH::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONDOWN; /* For normal mice etc. */
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONDOWN;
+#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
+ else if (ev.button.button == SDL_BUTTON_WHEELUP)
+ event.type = Common::EVENT_WHEELUP;
+ else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
+ event.type = Common::EVENT_WHEELDOWN;
+#endif
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONDOWN;
+#endif
+ else
+ return false;
+
+ fillMouseEvent(event, ev.button.x, ev.button.y);
+
+ return true;
+}
+
+bool OSystem_GPH::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (GPH::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONUP; /* For normal mice etc. */
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONUP;
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONUP;
+#endif
+ else
+ return false;
+
+ fillMouseEvent(event, ev.button.x, ev.button.y);
+
+ return true;
+}
+
+/* Custom handleJoyButtonDown/handleJoyButtonUp to deal with the joystick buttons on GPH devices */
+
+bool OSystem_GPH::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
+
+ _stickBtn[ev.jbutton.button] = 1;
+ event.kbd.flags = 0;
+
+ switch (ev.jbutton.button) {
+ case BUTTON_UP:
+ case BUTTON_UPLEFT:
+ case BUTTON_LEFT:
+ case BUTTON_DOWNLEFT:
+ case BUTTON_DOWN:
+ case BUTTON_DOWNRIGHT:
+ case BUTTON_RIGHT:
+ case BUTTON_UPRIGHT:
+ moveStick();
+ event.type = Common::EVENT_MOUSEMOVE;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_B:
+ case BUTTON_CLICK:
+ event.type = Common::EVENT_LBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_X:
+ event.type = Common::EVENT_RBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_L:
+ BUTTON_STATE_L = true;
+ break;
+ case BUTTON_R:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+#ifdef ENABLE_VKEYBD
+ event.kbd.keycode = Common::KEYCODE_F7;
+ event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
+#else
+ event.kbd.keycode = Common::KEYCODE_0;
+ event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
+#endif
+ } else {
+ event.kbd.keycode = Common::KEYCODE_RETURN;
+ event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_SELECT:
+ case BUTTON_HOME:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_QUIT;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_A:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_PREDICTIVE_DIALOG;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_PERIOD;
+ event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_Y:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ GPH::ToggleTapMode();
+ if (GPH::tapmodeLevel == TAPMODE_LEFT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Left Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_RIGHT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Right Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_HOVER) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Hover (No Click)");
+ }
+ } else {
+ event.kbd.keycode = Common::KEYCODE_SPACE;
+ event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_MENU:
+ case BUTTON_HELP:
+ event.type = Common::EVENT_KEYDOWN;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_MAINMENU;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_F5;
+ event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_VOLUP:
+ WIZ_HW::mixerMoveVolume(2);
+ if (WIZ_HW::volumeLevel == 100) {
+ displayMessageOnOSD("Maximum Volume");
+ } else {
+ displayMessageOnOSD("Increasing Volume");
+ }
+ break;
+ case BUTTON_VOLDOWN:
+ WIZ_HW::mixerMoveVolume(1);
+ if (WIZ_HW::volumeLevel == 0) {
+ displayMessageOnOSD("Minimal Volume");
+ } else {
+ displayMessageOnOSD("Decreasing Volume");
+ }
+ break;
+ case BUTTON_HOLD:
+ event.type = Common::EVENT_QUIT;
+ break;
+ case BUTTON_HELP2:
+ GPH::ToggleTapMode();
+ if (GPH::tapmodeLevel == TAPMODE_LEFT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Left Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_RIGHT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Right Click");
+ } else if (GPH::tapmodeLevel == TAPMODE_HOVER) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode': Hover (No Click)");
+ }
+ break;
+ }
+ return true;
+}
+
+bool OSystem_GPH::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
+
+ _stickBtn[ev.jbutton.button] = 0;
+ event.kbd.flags = 0;
+
+ switch (ev.jbutton.button) {
+ case BUTTON_UP:
+ case BUTTON_UPLEFT:
+ case BUTTON_LEFT:
+ case BUTTON_DOWNLEFT:
+ case BUTTON_DOWN:
+ case BUTTON_DOWNRIGHT:
+ case BUTTON_RIGHT:
+ case BUTTON_UPRIGHT:
+ moveStick();
+ event.type = Common::EVENT_MOUSEMOVE;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_B:
+ case BUTTON_CLICK:
+ event.type = Common::EVENT_LBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_X:
+ event.type = Common::EVENT_RBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ break;
+ case BUTTON_L:
+ BUTTON_STATE_L = false;
+ break;
+ case BUTTON_SELECT:
+ case BUTTON_HOME:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_ESCAPE;
+ event.kbd.ascii = mapKey(SDLK_ESCAPE, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_A:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_PERIOD;
+ event.kbd.ascii = mapKey(SDLK_PERIOD, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_Y:
+ event.type = Common::EVENT_KEYUP;
+ event.kbd.keycode = Common::KEYCODE_SPACE;
+ event.kbd.ascii = mapKey(SDLK_SPACE, ev.key.keysym.mod, 0);
+ break;
+ case BUTTON_MENU:
+ case BUTTON_HELP:
+ event.type = Common::EVENT_KEYUP;
+ if (BUTTON_STATE_L == true) {
+ event.type = Common::EVENT_MAINMENU;
+ } else {
+ event.kbd.keycode = Common::KEYCODE_F5;
+ event.kbd.ascii = mapKey(SDLK_F5, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_R:
+ event.type = Common::EVENT_KEYUP;
+ if (BUTTON_STATE_L == true) {
+#ifdef ENABLE_VKEYBD
+ event.kbd.keycode = Common::KEYCODE_F7;
+ event.kbd.ascii = mapKey(SDLK_F7, ev.key.keysym.mod, 0);
+#else
+ event.kbd.keycode = Common::KEYCODE_0;
+ event.kbd.ascii = mapKey(SDLK_0, ev.key.keysym.mod, 0);
+#endif
+ } else {
+ event.kbd.keycode = Common::KEYCODE_RETURN;
+ event.kbd.ascii = mapKey(SDLK_RETURN, ev.key.keysym.mod, 0);
+ }
+ break;
+ case BUTTON_VOLUP:
+ break;
+ case BUTTON_VOLDOWN:
+ break;
+ case BUTTON_HOLD:
+ break;
+ case BUTTON_HELP2:
+ break;
+ }
+ return true;
+}
+
+bool OSystem_GPH::remapKey(SDL_Event &ev,Common::Event &event) {
+ return false;
+}
diff --git a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp b/backends/platform/gph/gph-graphics.cpp
index 9b8a41a7cf..8fada7e40a 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp
+++ b/backends/platform/gph/gph-graphics.cpp
@@ -23,7 +23,7 @@
*
*/
-#include "backends/platform/gp2xwiz/gp2xwiz-sdl.h"
+#include "backends/platform/gph/gph-sdl.h"
#include "common/mutex.h"
#include "graphics/font.h"
@@ -38,15 +38,15 @@ static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
};
-const OSystem::GraphicsMode *OSystem_GP2XWIZ::getSupportedGraphicsModes() const {
+const OSystem::GraphicsMode *OSystem_GPH::getSupportedGraphicsModes() const {
return s_supportedGraphicsModes;
}
-int OSystem_GP2XWIZ::getDefaultGraphicsMode() const {
+int OSystem_GPH::getDefaultGraphicsMode() const {
return GFX_NORMAL;
}
-bool OSystem_GP2XWIZ::setGraphicsMode(int mode) {
+bool OSystem_GPH::setGraphicsMode(int mode) {
Common::StackLock lock(_graphicsMutex);
assert(_transactionMode == kTransactionActive);
@@ -80,7 +80,7 @@ bool OSystem_GP2XWIZ::setGraphicsMode(int mode) {
return true;
}
-void OSystem_GP2XWIZ::setGraphicsModeIntern() {
+void OSystem_GPH::setGraphicsModeIntern() {
Common::StackLock lock(_graphicsMutex);
ScalerProc *newScalerProc = 0;
@@ -109,7 +109,7 @@ void OSystem_GP2XWIZ::setGraphicsModeIntern() {
blitCursor();
}
-void OSystem_GP2XWIZ::initSize(uint w, uint h) {
+void OSystem_GPH::initSize(uint w, uint h) {
assert(_transactionMode == kTransactionActive);
// Avoid redundant res changes
@@ -127,7 +127,7 @@ void OSystem_GP2XWIZ::initSize(uint w, uint h) {
_transactionDetails.sizeChanged = true;
}
-bool OSystem_GP2XWIZ::loadGFXMode() {
+bool OSystem_GPH::loadGFXMode() {
if (_videoMode.screenWidth > 320 || _videoMode.screenHeight > 240) {
_videoMode.aspectRatioCorrection = false;
setGraphicsMode(GFX_HALF);
@@ -155,7 +155,7 @@ bool OSystem_GP2XWIZ::loadGFXMode() {
return OSystem_SDL::loadGFXMode();
}
-void OSystem_GP2XWIZ::drawMouse() {
+void OSystem_GPH::drawMouse() {
if (!_mouseVisible || !_mouseSurface) {
_mouseBackup.x = _mouseBackup.y = _mouseBackup.w = _mouseBackup.h = 0;
return;
@@ -226,7 +226,7 @@ void OSystem_GP2XWIZ::drawMouse() {
addDirtyRect(dst.x, dst.y, dst.w, dst.h, true);
}
-void OSystem_GP2XWIZ::undrawMouse() {
+void OSystem_GPH::undrawMouse() {
const int x = _mouseBackup.x;
const int y = _mouseBackup.y;
@@ -244,7 +244,7 @@ void OSystem_GP2XWIZ::undrawMouse() {
}
}
-void OSystem_GP2XWIZ::internUpdateScreen() {
+void OSystem_GPH::internUpdateScreen() {
SDL_Surface *srcSurf, *origSurf;
int height, width;
ScalerProc *scalerProc;
@@ -256,7 +256,8 @@ void OSystem_GP2XWIZ::internUpdateScreen() {
#endif
// If the shake position changed, fill the dirty area with blackness
- if (_currentShakePos != _newShakePos) {
+ if (_currentShakePos != _newShakePos ||
+ (_mouseNeedsRedraw && _mouseBackup.y <= _currentShakePos)) {
SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor};
if (_videoMode.aspectRatioCorrection && !_overlayVisible)
@@ -314,6 +315,7 @@ void OSystem_GP2XWIZ::internUpdateScreen() {
width = _videoMode.overlayWidth;
height = _videoMode.overlayHeight;
scalerProc = Normal1x;
+
scale1 = 1;
}
@@ -441,7 +443,7 @@ void OSystem_GP2XWIZ::internUpdateScreen() {
_mouseNeedsRedraw = false;
}
-void OSystem_GP2XWIZ::showOverlay() {
+void OSystem_GPH::showOverlay() {
if (_videoMode.mode == GFX_HALF){
_mouseCurState.x = _mouseCurState.x / 2;
_mouseCurState.y = _mouseCurState.y / 2;
@@ -449,7 +451,7 @@ void OSystem_GP2XWIZ::showOverlay() {
OSystem_SDL::showOverlay();
}
-void OSystem_GP2XWIZ::hideOverlay() {
+void OSystem_GPH::hideOverlay() {
if (_videoMode.mode == GFX_HALF){
_mouseCurState.x = _mouseCurState.x * 2;
_mouseCurState.y = _mouseCurState.y * 2;
@@ -457,7 +459,7 @@ void OSystem_GP2XWIZ::hideOverlay() {
OSystem_SDL::hideOverlay();
}
-void OSystem_GP2XWIZ::warpMouse(int x, int y) {
+void OSystem_GPH::warpMouse(int x, int y) {
if (_mouseCurState.x != x || _mouseCurState.y != y) {
if (_videoMode.mode == GFX_HALF && !_overlayVisible){
x = x / 2;
diff --git a/backends/platform/gp2xwiz/gp2xwiz-hw.cpp b/backends/platform/gph/gph-hw.cpp
index bc1aa00ce4..fa52526f01 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-hw.cpp
+++ b/backends/platform/gph/gph-hw.cpp
@@ -28,7 +28,10 @@
*
*/
-#include "backends/platform/gp2xwiz/gp2xwiz-hw.h"
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/platform/gph/gph-hw.h"
#include <fcntl.h>
#include <signal.h>
@@ -82,3 +85,28 @@ void mixerMoveVolume(int direction) {
}
} /* namespace WIZ_HW */
+
+namespace GPH {
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
+};
+
+int tapmodeLevel = TAPMODE_LEFT;
+
+void ToggleTapMode() {
+ if (tapmodeLevel == TAPMODE_LEFT) {
+ tapmodeLevel = TAPMODE_RIGHT;
+ } else if (tapmodeLevel == TAPMODE_RIGHT) {
+ tapmodeLevel = TAPMODE_HOVER;
+ } else if (tapmodeLevel == TAPMODE_HOVER) {
+ tapmodeLevel = TAPMODE_LEFT;
+ } else {
+ tapmodeLevel = TAPMODE_LEFT;
+ }
+}
+
+} /* namespace GPH */
diff --git a/backends/platform/gp2xwiz/gp2xwiz-hw.h b/backends/platform/gph/gph-hw.h
index 507841e902..7276276608 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-hw.h
+++ b/backends/platform/gph/gph-hw.h
@@ -28,8 +28,8 @@
*
*/
-#ifndef GP2XWIZ_HW_H
-#define GP2XWIZ_HW_H
+#ifndef GPH_HW_H
+#define GPH_HW_H
namespace WIZ_HW {
@@ -41,4 +41,12 @@ extern void mixerMoveVolume(int);
} /* namespace WIZ_HW */
-#endif //GP2XWIZ_HW_H
+namespace GPH {
+
+extern int tapmodeLevel;
+
+extern void ToggleTapMode();
+
+} /* namespace GPH */
+
+#endif //GPH_HW_H
diff --git a/backends/platform/gp2xwiz/gp2xwiz-main.cpp b/backends/platform/gph/gph-main.cpp
index 839afa6a95..c433ba9f3f 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-main.cpp
+++ b/backends/platform/gph/gph-main.cpp
@@ -23,11 +23,15 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#include <SDL/SDL.h>
-#include "backends/platform/gp2xwiz/gp2xwiz-sdl.h"
-#include "backends/platform/gp2xwiz/gp2xwiz-hw.h"
+// #include "backends/platform/gph/gph-options.h"
+#include "backends/platform/gph/gph-sdl.h"
+#include "backends/platform/gph/gph-hw.h"
#include "backends/plugins/posix/posix-provider.h"
#include "base/main.h"
@@ -57,7 +61,7 @@
#define DUMP_STDOUT
int main(int argc, char *argv[]) {
- g_system = new OSystem_GP2XWIZ();
+ g_system = new OSystem_GPH();
assert(g_system);
#ifdef DYNAMIC_MODULES
PluginManager::instance().addPluginProvider(new POSIXPluginProvider());
@@ -70,7 +74,7 @@ int main(int argc, char *argv[]) {
return res;
}
-void OSystem_GP2XWIZ::initBackend() {
+void OSystem_GPH::initBackend() {
/* Setup default save path to be workingdir/saves */
@@ -158,13 +162,16 @@ void OSystem_GP2XWIZ::initBackend() {
/* Make sure SDL knows that we have a joystick we want to use. */
ConfMan.setInt("joystick_num", 0);
+ /* Now setup any device specific user options (Left handed mode, that sort of thing). */
+ // GPH::setOptions();
+
printf("%s\n", "Passing to OSystem::SDL initBackend.");
/* Pass to SDL backend to do the heavy lifting */
OSystem_SDL::initBackend();
}
-void OSystem_GP2XWIZ::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+void OSystem_GPH::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
/* Setup default extra data paths for engine data files and plugins */
char workDirName[PATH_MAX+1];
@@ -199,7 +206,7 @@ void OSystem_GP2XWIZ::addSysArchivesToSearchSet(Common::SearchSet &s, int priori
}
}
-void OSystem_GP2XWIZ::quit() {
+void OSystem_GPH::quit() {
WIZ_HW::deviceDeinit();
diff --git a/backends/platform/gp2xwiz/gp2xwiz-sdl.h b/backends/platform/gph/gph-sdl.h
index e312d0f26d..136363f9a0 100644
--- a/backends/platform/gp2xwiz/gp2xwiz-sdl.h
+++ b/backends/platform/gph/gph-sdl.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef GP2XWIZ_SDL_H
-#define GP2XWIZ_SDL_H
+#ifndef GPH_SDL_H
+#define GPH_SDL_H
#include "backends/platform/sdl/sdl.h"
@@ -40,9 +40,9 @@ enum {
#define PATH_MAX 255
#endif
-class OSystem_GP2XWIZ : public OSystem_SDL {
+class OSystem_GPH : public OSystem_SDL {
public:
- OSystem_GP2XWIZ() {}
+ OSystem_GPH() {}
/* Graphics */
void initSize(uint w, uint h);
@@ -59,7 +59,6 @@ public:
void hideOverlay();
/* Event Stuff */
- bool pollEvent(Common::Event &event);
void moveStick();
void fillMouseEvent(Common::Event&, int, int);
void warpMouse(int, int);
@@ -72,6 +71,11 @@ public:
protected:
bool _stickBtn[32];
+
+ bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event);
+ bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event);
+ bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event);
+ bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event);
};
-#endif
+#endif //GPH_SDL_H
diff --git a/backends/platform/gp2xwiz/module.mk b/backends/platform/gph/module.mk
index edf2f2a717..f5567f581e 100644
--- a/backends/platform/gp2xwiz/module.mk
+++ b/backends/platform/gph/module.mk
@@ -1,10 +1,10 @@
-MODULE := backends/platform/gp2xwiz
+MODULE := backends/platform/gph
MODULE_OBJS := \
- gp2xwiz-events.o \
- gp2xwiz-graphics.o \
- gp2xwiz-hw.o \
- gp2xwiz-main.o
+ gph-events.o \
+ gph-graphics.o \
+ gph-hw.o \
+ gph-main.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
diff --git a/backends/platform/iphone/iphone_common.h b/backends/platform/iphone/iphone_common.h
index 1f5ed01982..7c7770f443 100644
--- a/backends/platform/iphone/iphone_common.h
+++ b/backends/platform/iphone/iphone_common.h
@@ -67,12 +67,19 @@ void iphone_main(int argc, char *argv[]);
#endif
// On the ObjC side
-void iPhone_updateScreen();
+void iPhone_updateScreen(int mouseX, int mouseY);
void iPhone_updateScreenRect(unsigned short* screen, int x1, int y1, int x2, int y2);
+void iPhone_updateOverlayRect(unsigned short* screen, int x1, int y1, int x2, int y2);
void iPhone_initSurface(int width, int height);
bool iPhone_fetchEvent(int *outEvent, float *outX, float *outY);
const char* iPhone_getDocumentsDir();
bool iPhone_isHighResDevice();
+int iPhone_getScreenHeight();
+int iPhone_getScreenWidth();
+void iPhone_enableOverlay(int state);
+void iPhone_setMouseCursor(short* buffer, int width, int height);
+
+uint getSizeNextPOT(uint size);
#ifdef __cplusplus
}
diff --git a/backends/platform/iphone/iphone_main.m b/backends/platform/iphone/iphone_main.m
index d8992de5bb..1fb2cc3788 100644
--- a/backends/platform/iphone/iphone_main.m
+++ b/backends/platform/iphone/iphone_main.m
@@ -103,13 +103,24 @@ int main(int argc, char** argv) {
[NSThread detachNewThreadSelector:@selector(mainLoop:) toTarget:self withObject:nil];
}
+- (void)applicationDidResume
+{
+}
+
+- (void)applicationWillSuspend
+{
+}
+
+- (void)applicationWillTerminate
+{
+}
+
- (void)applicationSuspend:(struct __GSEvent *)event {
//[self setApplicationBadge:NSLocalizedString(@"ON", nil)];
[_view applicationSuspend];
}
- (void)applicationResume:(struct __GSEvent *)event {
- [self removeApplicationBadge];
[_view applicationResume];
// Workaround, need to "hide" and unhide the statusbar to properly remove it,
diff --git a/backends/platform/iphone/iphone_video.h b/backends/platform/iphone/iphone_video.h
index 1060a2a223..aed15ecfd5 100644
--- a/backends/platform/iphone/iphone_video.h
+++ b/backends/platform/iphone/iphone_video.h
@@ -54,6 +54,8 @@
GLint _visibleWidth;
GLint _visibleHeight;
GLuint _screenTexture;
+ GLuint _overlayTexture;
+ GLuint _mouseCursorTexture;
}
- (id)initWithFrame:(struct CGRect)frame;
@@ -65,6 +67,11 @@
- (void)initSurface;
- (void)updateSurface;
+- (void)updateMainSurface;
+- (void)updateOverlaySurface;
+- (void)updateMouseSurface;
+
+-(void)updateMouseCursor;
- (id)getEvent;
diff --git a/backends/platform/iphone/iphone_video.m b/backends/platform/iphone/iphone_video.m
index cd8b38acb3..821d3de634 100644
--- a/backends/platform/iphone/iphone_video.m
+++ b/backends/platform/iphone/iphone_video.m
@@ -32,23 +32,88 @@ static int _height = 0;
static int _fullWidth;
static int _fullHeight;
static CGRect _screenRect;
+
static char* _textureBuffer = 0;
static int _textureWidth = 0;
static int _textureHeight = 0;
+
+static char* _overlayTexBuffer = 0;
+static int _overlayTexWidth = 0;
+static int _overlayTexHeight = 0;
+static int _overlayWidth = 0;
+static int _overlayHeight = 0;
+static float _overlayPortraitRatio = 1.0f;
+
NSLock* _lock = nil;
static int _needsScreenUpdate = 0;
+static int _overlayIsEnabled = 0;
static UITouch* _firstTouch = NULL;
static UITouch* _secondTouch = NULL;
+static short* _mouseCursor = NULL;
+static int _mouseCursorHeight = 0;
+static int _mouseCursorWidth = 0;
+static int _mouseX = 0;
+static int _mouseY = 0;
+
// static long lastTick = 0;
// static int frames = 0;
+#define printOpenGLError() printOglError(__FILE__, __LINE__)
+
+int printOglError(const char *file, int line)
+{
+ int retCode = 0;
+
+ // returns 1 if an OpenGL error occurred, 0 otherwise.
+ GLenum glErr = glGetError();
+ while( glErr != GL_NO_ERROR)
+ {
+ fprintf(stderr, "glError: %u (%s: %d)\n", glErr, file, line );
+ retCode = 1;
+ glErr = glGetError();
+ }
+ return retCode;
+}
+
+void iPhone_setMouseCursor(short* buffer, int width, int height) {
+ _mouseCursor = buffer;
+
+ _mouseCursorWidth = width;
+ _mouseCursorHeight = height;
+
+ [sharedInstance performSelectorOnMainThread:@selector(updateMouseCursor) withObject:nil waitUntilDone: YES];
+}
+
+void iPhone_enableOverlay(int state) {
+ _overlayIsEnabled = state;
+}
+
+int iPhone_getScreenHeight() {
+ return _overlayHeight;
+}
+
+int iPhone_getScreenWidth() {
+ return _overlayWidth;
+}
+
bool iPhone_isHighResDevice() {
return _fullHeight > 480;
}
-void iPhone_updateScreen() {
+void iPhone_updateScreen(int mouseX, int mouseY) {
+ //printf("Mouse: (%i, %i)\n", mouseX, mouseY);
+
+ //_mouseX = _overlayHeight - (float)mouseX / _width * _overlayHeight;
+ //_mouseY = (float)mouseY / _height * _overlayWidth;
+
+ //_mouseX = _overlayHeight - mouseX;
+ //_mouseY = mouseY;
+
+ _mouseX = (_overlayWidth - mouseX) / (float)_overlayWidth * _overlayHeight;
+ _mouseY = mouseY / (float)_overlayHeight * _overlayWidth;
+
if (!_needsScreenUpdate) {
_needsScreenUpdate = 1;
[sharedInstance performSelectorOnMainThread:@selector(updateSurface) withObject:nil waitUntilDone: NO];
@@ -56,16 +121,17 @@ void iPhone_updateScreen() {
}
void iPhone_updateScreenRect(unsigned short* screen, int x1, int y1, int x2, int y2) {
- //[_lock lock];
-
int y;
- for (y = y1; y < y2; ++y) {
+ for (y = y1; y < y2; ++y)
memcpy(&_textureBuffer[(y * _textureWidth + x1 )* 2], &screen[y * _width + x1], (x2 - x1) * 2);
- }
-
- //[_lock unlock];
}
+void iPhone_updateOverlayRect(unsigned short* screen, int x1, int y1, int x2, int y2) {
+ int y;
+ //printf("Overlaywidth: %u, fullwidth %u\n", _overlayWidth, _fullWidth);
+ for (y = y1; y < y2; ++y)
+ memcpy(&_overlayTexBuffer[(y * _overlayTexWidth + x1 )* 2], &screen[y * _overlayWidth + x1], (x2 - x1) * 2);
+}
void iPhone_initSurface(int width, int height) {
_width = width;
@@ -92,6 +158,19 @@ bool iPhone_fetchEvent(int *outEvent, float *outX, float *outY) {
return true;
}
+uint getSizeNextPOT(uint size) {
+ if ((size & (size - 1)) || !size) {
+ int log = 0;
+
+ while (size >>= 1)
+ ++log;
+
+ size = (2 << log);
+ }
+
+ return size;
+}
+
const char* iPhone_getDocumentsDir() {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
@@ -110,18 +189,6 @@ bool getLocalMouseCoords(CGPoint *point) {
return true;
}
-uint getSizeNextPOT(uint size) {
- if ((size & (size - 1)) || !size) {
- int log = 0;
-
- while (size >>= 1)
- ++log;
-
- size = (2 << log);
- }
-
- return size;
-}
@implementation iPhoneView
@@ -131,7 +198,15 @@ uint getSizeNextPOT(uint size) {
}
- (id)initWithFrame:(struct CGRect)frame {
- [super initWithFrame: frame];
+ self = [super initWithFrame: frame];
+
+ if([[UIScreen mainScreen] respondsToSelector: NSSelectorFromString(@"scale")])
+ {
+ if([self respondsToSelector: NSSelectorFromString(@"contentScaleFactor")])
+ {
+ //self.contentScaleFactor = [[UIScreen mainScreen] scale];
+ }
+ }
_fullWidth = frame.size.width;
_fullHeight = frame.size.height;
@@ -143,6 +218,8 @@ uint getSizeNextPOT(uint size) {
_keyboardView = nil;
_context = nil;
_screenTexture = 0;
+ _overlayTexture = 0;
+ _mouseCursorTexture = 0;
return self;
}
@@ -156,6 +233,8 @@ uint getSizeNextPOT(uint size) {
if (_screenTexture)
free(_textureBuffer);
+
+ free(_overlayTexBuffer);
}
- (void *)getSurface {
@@ -181,6 +260,38 @@ uint getSizeNextPOT(uint size) {
}
_needsScreenUpdate = 0;
+ if (_overlayIsEnabled) {
+ glClear(GL_COLOR_BUFFER_BIT); printOpenGLError();
+ }
+
+ [self updateMainSurface];
+
+ if (_overlayIsEnabled) {
+ [self updateOverlaySurface];
+ [self updateMouseSurface];
+ }
+
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); printOpenGLError();
+ [_context presentRenderbuffer:GL_RENDERBUFFER_OES];
+
+}
+
+-(void)updateMouseCursor {
+ if (_mouseCursorTexture == 0) {
+ glGenTextures(1, &_mouseCursorTexture); printOpenGLError();
+ glBindTexture(GL_TEXTURE_2D, _mouseCursorTexture); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); printOpenGLError();
+ }
+
+ glBindTexture(GL_TEXTURE_2D, _mouseCursorTexture); printOpenGLError();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSizeNextPOT(_mouseCursorWidth), getSizeNextPOT(_mouseCursorHeight), 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _mouseCursor); printOpenGLError();
+
+ free(_mouseCursor);
+ _mouseCursor = NULL;
+}
+
+- (void)updateMainSurface {
GLfloat vertices[] = {
0.0f + _heightOffset, 0.0f + _widthOffset,
_visibleWidth - _heightOffset, 0.0f + _widthOffset,
@@ -198,20 +309,76 @@ uint getSizeNextPOT(uint size) {
0.0f, texHeight
};
- glVertexPointer(2, GL_FLOAT, 0, vertices);
- glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ glVertexPointer(2, GL_FLOAT, 0, vertices); printOpenGLError();
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords); printOpenGLError();
+
+ glBindTexture(GL_TEXTURE_2D, _screenTexture); printOpenGLError();
- //[_lock lock];
// Unfortunately we have to update the whole texture every frame, since glTexSubImage2D is actually slower in all cases
// due to the iPhone internals having to convert the whole texture back from its internal format when used.
// In the future we could use several tiled textures instead.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureWidth, _textureHeight, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _textureBuffer);
- //[_lock unlock];
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureWidth, _textureHeight, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, _textureBuffer); printOpenGLError();
+ glDisable(GL_BLEND);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
+}
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
- [_context presentRenderbuffer:GL_RENDERBUFFER_OES];
+- (void)updateOverlaySurface {
+ GLfloat vertices[] = {
+ 0.0f, 0.0f,
+ _overlayHeight, 0.0f,
+ 0.0f, _overlayWidth * _overlayPortraitRatio,
+ _overlayHeight, _overlayWidth * _overlayPortraitRatio
+ };
+
+ float texWidth = _overlayWidth / (float)_overlayTexWidth;
+ float texHeight = _overlayHeight / (float)_overlayTexHeight;
+ const GLfloat texCoords[] = {
+ texWidth, 0.0f,
+ 0.0f, 0.0f,
+ texWidth, texHeight,
+ 0.0f, texHeight
+ };
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices); printOpenGLError();
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords); printOpenGLError();
+
+ glBindTexture(GL_TEXTURE_2D, _overlayTexture); printOpenGLError();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _overlayTexWidth, _overlayTexHeight, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, _overlayTexBuffer); printOpenGLError();
+ glEnable(GL_BLEND);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
+}
+
+- (void)updateMouseSurface {
+
+ int width = _mouseCursorWidth / (float)_backingWidth * _backingHeight;
+ int height = _mouseCursorHeight / (float)_backingHeight * _backingWidth;
+
+ GLfloat vertices[] = {
+ _mouseX, _mouseY,
+ _mouseX + height, _mouseY,
+ _mouseX, _mouseY + width,
+ _mouseX + height, _mouseY + width
+ };
+
+ //printf("Cursor: width %u height %u\n", _mouseCursorWidth, _mouseCursorHeight);
+
+ float texWidth = _mouseCursorWidth / (float)getSizeNextPOT(_mouseCursorWidth);
+ float texHeight = _mouseCursorHeight / (float)getSizeNextPOT(_mouseCursorHeight);
+
+ const GLfloat texCoords[] = {
+ texWidth, 0.0f,
+ 0.0f, 0.0f,
+ texWidth, texHeight,
+ 0.0f, texHeight
+ };
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices); printOpenGLError();
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords); printOpenGLError();
+
+ glBindTexture(GL_TEXTURE_2D, _mouseCursorTexture); printOpenGLError();
+ glEnable(GL_BLEND);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); printOpenGLError();
}
- (void)initSurface {
@@ -232,28 +399,39 @@ uint getSizeNextPOT(uint size) {
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!_context || [EAGLContext setCurrentContext:_context]) {
- glGenFramebuffersOES(1, &_viewFramebuffer);
- glGenRenderbuffersOES(1, &_viewRenderbuffer);
+ glGenFramebuffersOES(1, &_viewFramebuffer); printOpenGLError();
+ glGenRenderbuffersOES(1, &_viewRenderbuffer); printOpenGLError();
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, _viewFramebuffer); printOpenGLError();
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); printOpenGLError();
[_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _viewRenderbuffer);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _viewRenderbuffer); printOpenGLError();
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &_backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &_backingHeight);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &_backingWidth); printOpenGLError();
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &_backingHeight); printOpenGLError();
if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
NSLog(@"Failed to make complete framebuffer object %x.", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return;
}
- glViewport(0, 0, _backingWidth, _backingHeight);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ _overlayHeight = _backingWidth;
+ _overlayWidth = _backingHeight;
+ _overlayTexWidth = getSizeNextPOT(_overlayHeight);
+ _overlayTexHeight = getSizeNextPOT(_overlayWidth);
+
+ int textureSize = _overlayTexWidth * _overlayTexHeight * 2;
+ _overlayTexBuffer = (char *)malloc(textureSize);
+ memset(_overlayTexBuffer, 0, textureSize);
- glEnable(GL_TEXTURE_2D);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glEnableClientState(GL_VERTEX_ARRAY);
+ glViewport(0, 0, _backingWidth, _backingHeight); printOpenGLError();
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); printOpenGLError();
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glEnable(GL_TEXTURE_2D); printOpenGLError();
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY); printOpenGLError();
+ glEnableClientState(GL_VERTEX_ARRAY); printOpenGLError();
}
}
@@ -261,22 +439,32 @@ uint getSizeNextPOT(uint size) {
glLoadIdentity();
if (orientation == UIDeviceOrientationLandscapeRight) {
- glRotatef(-90, 0, 0, 1);
+ glRotatef(-90, 0, 0, 1); printOpenGLError();
} else if (orientation == UIDeviceOrientationLandscapeLeft) {
- glRotatef(90, 0, 0, 1);
+ glRotatef(90, 0, 0, 1); printOpenGLError();
} else {
- glRotatef(180, 0, 0, 1);
+ glRotatef(180, 0, 0, 1); printOpenGLError();
}
- glOrthof(0, _backingWidth, 0, _backingHeight, 0, 1);
+ glOrthof(0, _backingWidth, 0, _backingHeight, 0, 1); printOpenGLError();
if (_screenTexture > 0) {
- glDeleteTextures(1, &_screenTexture);
+ glDeleteTextures(1, &_screenTexture); printOpenGLError();
+ }
+
+ glGenTextures(1, &_screenTexture); printOpenGLError();
+ glBindTexture(GL_TEXTURE_2D, _screenTexture); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); printOpenGLError();
+
+ if (_overlayTexture > 0) {
+ glDeleteTextures(1, &_overlayTexture); printOpenGLError();
}
- glGenTextures(1, &_screenTexture);
- glBindTexture(GL_TEXTURE_2D, _screenTexture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glGenTextures(1, &_overlayTexture); printOpenGLError();
+ glBindTexture(GL_TEXTURE_2D, _overlayTexture); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); printOpenGLError();
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); printOpenGLError();
if (_textureBuffer) {
free(_textureBuffer);
@@ -286,12 +474,12 @@ uint getSizeNextPOT(uint size) {
_textureBuffer = (char*)malloc(textureSize);
memset(_textureBuffer, 0, textureSize);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, _viewRenderbuffer); printOpenGLError();
// The color buffer is triple-buffered, so we clear it multiple times right away to avid doing any glClears later.
int clearCount = 5;
while (clearCount-- > 0) {
- glClear(GL_COLOR_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT); printOpenGLError();
[_context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
@@ -320,6 +508,7 @@ uint getSizeNextPOT(uint size) {
//printf("Rect: %i, %i, %i, %i\n", _widthOffset, _heightOffset, rectWidth, rectHeight);
_screenRect = CGRectMake(_widthOffset, _heightOffset, rectWidth, rectHeight);
+ _overlayPortraitRatio = 1.0f;
} else {
float ratio = (float)_height / (float)_width;
int height = _fullWidth * ratio;
@@ -340,6 +529,7 @@ uint getSizeNextPOT(uint size) {
[self addSubview:[_keyboardView inputView]];
[self addSubview: _keyboardView];
[[_keyboardView inputView] becomeFirstResponder];
+ _overlayPortraitRatio = (_overlayHeight * ratio) / _overlayWidth;
}
}
@@ -421,7 +611,7 @@ uint getSizeNextPOT(uint size) {
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
- NSSet *allTouches = [event allTouches];
+ //NSSet *allTouches = [event allTouches];
for (UITouch* touch in touches) {
if (touch == _firstTouch) {
diff --git a/backends/platform/iphone/osys_events.cpp b/backends/platform/iphone/osys_events.cpp
index c1c7ffdc0c..c30e34dd05 100644
--- a/backends/platform/iphone/osys_events.cpp
+++ b/backends/platform/iphone/osys_events.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "gui/message.h"
#include "common/translation.h"
@@ -60,16 +63,31 @@ bool OSystem_IPHONE::pollEvent(Common::Event &event) {
int y = 0;
switch (_screenOrientation) {
case kScreenOrientationPortrait:
- x = (int)(xUnit * _screenWidth);
- y = (int)(yUnit * _screenHeight);
+ if (_overlayVisible) {
+ x = (int)(xUnit * _overlayWidth);
+ y = (int)(yUnit * _overlayHeight);
+ } else {
+ x = (int)(xUnit * _screenWidth);
+ y = (int)(yUnit * _screenHeight);
+ }
break;
case kScreenOrientationLandscape:
- x = (int)(yUnit * _screenWidth);
- y = (int)((1.0 - xUnit) * _screenHeight);
+ if (_overlayVisible) {
+ x = (int)(yUnit * _overlayWidth);
+ y = (int)((1.0 - xUnit) * _overlayHeight);
+ } else {
+ x = (int)(yUnit * _screenWidth);
+ y = (int)((1.0 - xUnit) * _screenHeight);
+ }
break;
case kScreenOrientationFlippedLandscape:
- x = (int)((1.0 - yUnit) * _screenWidth);
- y = (int)(xUnit * _screenHeight);
+ if (_overlayVisible) {
+ x = (int)((1.0 - yUnit) * _overlayWidth);
+ y = (int)(xUnit * _overlayHeight);
+ } else {
+ x = (int)((1.0 - yUnit) * _screenWidth);
+ y = (int)(xUnit * _screenHeight);
+ }
break;
}
@@ -262,15 +280,18 @@ bool OSystem_IPHONE::handleEvent_mouseDragged(Common::Event &event, int x, int y
mouseNewPosX = (int)(_mouseX - deltaX / 0.5f);
mouseNewPosY = (int)(_mouseY - deltaY / 0.5f);
+ int widthCap = _overlayVisible ? _overlayWidth : _screenWidth;
+ int heightCap = _overlayVisible ? _overlayHeight : _screenHeight;
+
if (mouseNewPosX < 0)
mouseNewPosX = 0;
- else if (mouseNewPosX > _screenWidth)
- mouseNewPosX = _screenWidth;
+ else if (mouseNewPosX > widthCap)
+ mouseNewPosX = widthCap;
if (mouseNewPosY < 0)
mouseNewPosY = 0;
- else if (mouseNewPosY > _screenHeight)
- mouseNewPosY = _screenHeight;
+ else if (mouseNewPosY > heightCap)
+ mouseNewPosY = heightCap;
} else {
mouseNewPosX = x;
diff --git a/backends/platform/iphone/osys_main.cpp b/backends/platform/iphone/osys_main.cpp
index 6c26b6ca8d..9dc4e202c4 100644
--- a/backends/platform/iphone/osys_main.cpp
+++ b/backends/platform/iphone/osys_main.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include <unistd.h>
#include <pthread.h>
@@ -54,13 +57,13 @@ void *OSystem_IPHONE::s_soundParam = NULL;
OSystem_IPHONE::OSystem_IPHONE() :
_savefile(NULL), _mixer(NULL), _timer(NULL), _offscreen(NULL),
- _overlayVisible(false), _overlayBuffer(NULL), _fullscreen(NULL),
+ _overlayVisible(false), _fullscreen(NULL),
_mouseHeight(0), _mouseWidth(0), _mouseBuf(NULL), _lastMouseTap(0),
_secondaryTapped(false), _lastSecondaryTap(0), _screenOrientation(kScreenOrientationFlippedLandscape),
_needEventRestPeriod(false), _mouseClickAndDragEnabled(false),
_gestureStartX(-1), _gestureStartY(-1), _fullScreenIsDirty(false), _fullScreenOverlayIsDirty(false),
- _mouseDirty(false), _timeSuspended(0), _lastDragPosX(-1), _lastDragPosY(-1), _screenChangeCount(0)
-
+ _mouseDirty(false), _timeSuspended(0), _lastDragPosX(-1), _lastDragPosY(-1), _screenChangeCount(0),
+ _overlayHeight(0), _overlayWidth(0), _overlayBuffer(0)
{
_queuedInputEvent.type = (Common::EventType)0;
_lastDrawnMouseRect = Common::Rect(0, 0, 0, 0);
diff --git a/backends/platform/iphone/osys_main.h b/backends/platform/iphone/osys_main.h
index 3c80c83998..c925078b46 100644
--- a/backends/platform/iphone/osys_main.h
+++ b/backends/platform/iphone/osys_main.h
@@ -66,6 +66,9 @@ protected:
Graphics::Surface _framebuffer;
byte *_offscreen;
OverlayColor *_overlayBuffer;
+ uint16 _overlayHeight;
+ uint16 _overlayWidth;
+
uint16 *_fullscreen;
uint16 _palette[256];
@@ -144,7 +147,7 @@ public:
virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h);
virtual int16 getOverlayHeight();
virtual int16 getOverlayWidth();
- virtual Graphics::PixelFormat getOverlayFormat() const { return Graphics::createPixelFormat<565>(); }
+ virtual Graphics::PixelFormat getOverlayFormat() const { return Graphics::createPixelFormat<5551>(); }
virtual bool showMouse(bool visible);
diff --git a/backends/platform/iphone/osys_sound.cpp b/backends/platform/iphone/osys_sound.cpp
index 55892580f6..cd364f57ac 100644
--- a/backends/platform/iphone/osys_sound.cpp
+++ b/backends/platform/iphone/osys_sound.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "osys_main.h"
void OSystem_IPHONE::AQBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB) {
diff --git a/backends/platform/iphone/osys_video.cpp b/backends/platform/iphone/osys_video.cpp
index 76c2031758..88368a0eec 100644
--- a/backends/platform/iphone/osys_video.cpp
+++ b/backends/platform/iphone/osys_video.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "osys_main.h"
const OSystem::GraphicsMode* OSystem_IPHONE::getSupportedGraphicsModes() const {
@@ -57,10 +60,10 @@ void OSystem_IPHONE::initSize(uint width, uint height, const Graphics::PixelForm
_offscreen = (byte *)malloc(width * height);
bzero(_offscreen, width * height);
- free(_overlayBuffer);
+ //free(_overlayBuffer);
int fullSize = _screenWidth * _screenHeight * sizeof(OverlayColor);
- _overlayBuffer = (OverlayColor *)malloc(fullSize);
+ //_overlayBuffer = (OverlayColor *)malloc(fullSize);
clearOverlay();
free(_fullscreen);
@@ -70,6 +73,14 @@ void OSystem_IPHONE::initSize(uint width, uint height, const Graphics::PixelForm
iPhone_initSurface(width, height);
+ if (_overlayBuffer == NULL) {
+ _overlayHeight = iPhone_getScreenHeight();
+ _overlayWidth = iPhone_getScreenWidth();
+
+ printf("Overlay: (%u x %u)\n", _overlayWidth, _overlayHeight);
+ _overlayBuffer = new OverlayColor[_overlayHeight * _overlayWidth];
+ }
+
_fullScreenIsDirty = false;
dirtyFullScreen();
_mouseVisible = false;
@@ -187,7 +198,7 @@ void OSystem_IPHONE::updateScreen() {
_fullScreenIsDirty = false;
_fullScreenOverlayIsDirty = false;
- iPhone_updateScreen();
+ iPhone_updateScreen(_mouseX - _mouseHotspotX, _mouseY - _mouseHotspotY);
}
void OSystem_IPHONE::internUpdateScreen() {
@@ -222,8 +233,9 @@ void OSystem_IPHONE::internUpdateScreen() {
if (_overlayVisible)
drawDirtyOverlayRect(dirtyRect);
+ else
+ drawMouseCursorOnRectUpdate(dirtyRect, mouseRect);
- drawMouseCursorOnRectUpdate(dirtyRect, mouseRect);
updateHardwareSurfaceForRect(dirtyRect);
}
@@ -234,8 +246,8 @@ void OSystem_IPHONE::internUpdateScreen() {
//printf("Drawing: (%i, %i) -> (%i, %i)\n", dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
drawDirtyOverlayRect(dirtyRect);
- drawMouseCursorOnRectUpdate(dirtyRect, mouseRect);
- updateHardwareSurfaceForRect(dirtyRect);
+ //drawMouseCursorOnRectUpdate(dirtyRect, mouseRect);
+ //updateHardwareSurfaceForRect(dirtyRect);
}
}
}
@@ -256,16 +268,17 @@ void OSystem_IPHONE::drawDirtyRect(const Common::Rect& dirtyRect) {
}
void OSystem_IPHONE::drawDirtyOverlayRect(const Common::Rect& dirtyRect) {
- int h = dirtyRect.bottom - dirtyRect.top;
-
- uint16 *src = (uint16 *)&_overlayBuffer[dirtyRect.top * _screenWidth + dirtyRect.left];
- uint16 *dst = &_fullscreen[dirtyRect.top * _screenWidth + dirtyRect.left];
- int x = (dirtyRect.right - dirtyRect.left) * 2;
- for (int y = h; y > 0; y--) {
- memcpy(dst, src, x);
- src += _screenWidth;
- dst += _screenWidth;
- }
+ // int h = dirtyRect.bottom - dirtyRect.top;
+ //
+ // uint16 *src = (uint16 *)&_overlayBuffer[dirtyRect.top * _screenWidth + dirtyRect.left];
+ // uint16 *dst = &_fullscreen[dirtyRect.top * _screenWidth + dirtyRect.left];
+ // int x = (dirtyRect.right - dirtyRect.left) * 2;
+ // for (int y = h; y > 0; y--) {
+ // memcpy(dst, src, x);
+ // src += _screenWidth;
+ // dst += _screenWidth;
+ // }
+ iPhone_updateOverlayRect(_overlayBuffer, dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
}
void OSystem_IPHONE::drawMouseCursorOnRectUpdate(const Common::Rect& updatedRect, const Common::Rect& mouseRect) {
@@ -283,16 +296,19 @@ void OSystem_IPHONE::drawMouseCursorOnRectUpdate(const Common::Rect& updatedRect
srcY -= top;
top = 0;
}
- //int right = left + _mouseWidth;
+
int bottom = top + _mouseHeight;
if (bottom > _screenWidth)
bottom = _screenWidth;
- int displayWidth = _mouseWidth;
+
+ int displayWidth = _mouseWidth;
if (_mouseWidth + left > _screenWidth)
displayWidth = _screenWidth - left;
- int displayHeight = _mouseHeight;
+
+ int displayHeight = _mouseHeight;
if (_mouseHeight + top > _screenHeight)
displayHeight = _screenHeight - top;
+
byte *src = &_mouseBuf[srcY * _mouseWidth + srcX];
uint16 *dst = &_fullscreen[top * _screenWidth + left];
for (int y = displayHeight; y > srcY; y--) {
@@ -337,6 +353,7 @@ void OSystem_IPHONE::showOverlay() {
//printf("showOverlay()\n");
_overlayVisible = true;
dirtyFullOverlayScreen();
+ iPhone_enableOverlay(true);
}
void OSystem_IPHONE::hideOverlay() {
@@ -344,11 +361,12 @@ void OSystem_IPHONE::hideOverlay() {
_overlayVisible = false;
_dirtyOverlayRects.clear();
dirtyFullScreen();
+ iPhone_enableOverlay(false);
}
void OSystem_IPHONE::clearOverlay() {
//printf("clearOverlay()\n");
- bzero(_overlayBuffer, _screenWidth * _screenHeight * sizeof(OverlayColor));
+ bzero(_overlayBuffer, _overlayWidth * _overlayHeight * sizeof(OverlayColor));
dirtyFullOverlayScreen();
}
@@ -358,8 +376,8 @@ void OSystem_IPHONE::grabOverlay(OverlayColor *buf, int pitch) {
OverlayColor *src = _overlayBuffer;
do {
- memcpy(buf, src, _screenWidth * sizeof(OverlayColor));
- src += _screenWidth;
+ memcpy(buf, src, _overlayWidth * sizeof(OverlayColor));
+ src += _overlayWidth;
buf += pitch;
} while (--h);
}
@@ -380,11 +398,11 @@ void OSystem_IPHONE::copyRectToOverlay(const OverlayColor *buf, int pitch, int x
y = 0;
}
- if (w > _screenWidth - x)
- w = _screenWidth - x;
+ if (w > _overlayWidth - x)
+ w = _overlayWidth - x;
- if (h > _screenHeight - y)
- h = _screenHeight - y;
+ if (h > _overlayHeight - y)
+ h = _overlayHeight - y;
if (w <= 0 || h <= 0)
return;
@@ -393,24 +411,24 @@ void OSystem_IPHONE::copyRectToOverlay(const OverlayColor *buf, int pitch, int x
_dirtyOverlayRects.push_back(Common::Rect(x, y, x + w, y + h));
}
- OverlayColor *dst = _overlayBuffer + (y * _screenWidth + x);
- if (_screenWidth == pitch && pitch == w)
+ OverlayColor *dst = _overlayBuffer + (y * _overlayWidth + x);
+ if (_overlayWidth == pitch && pitch == w)
memcpy(dst, buf, h * w * sizeof(OverlayColor));
else {
do {
memcpy(dst, buf, w * sizeof(OverlayColor));
buf += pitch;
- dst += _screenWidth;
+ dst += _overlayWidth;
} while (--h);
}
}
int16 OSystem_IPHONE::getOverlayHeight() {
- return _screenHeight;
+ return _overlayHeight;
}
int16 OSystem_IPHONE::getOverlayWidth() {
- return _screenWidth;
+ return _overlayWidth;
}
bool OSystem_IPHONE::showMouse(bool visible) {
@@ -440,13 +458,31 @@ void OSystem_IPHONE::dirtyFullScreen() {
void OSystem_IPHONE::dirtyFullOverlayScreen() {
if (!_fullScreenOverlayIsDirty) {
_dirtyOverlayRects.clear();
- _dirtyOverlayRects.push_back(Common::Rect(0, 0, _screenWidth, _screenHeight));
+ _dirtyOverlayRects.push_back(Common::Rect(0, 0, _overlayWidth, _overlayHeight));
_fullScreenOverlayIsDirty = true;
}
}
void OSystem_IPHONE::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) {
- //printf("setMouseCursor(%i, %i)\n", hotspotX, hotspotY);
+ //printf("setMouseCursor(%i, %i, scale %u)\n", hotspotX, hotspotY, cursorTargetScale);
+
+ int texWidth = getSizeNextPOT(w);
+ int texHeight = getSizeNextPOT(h);
+ int bufferSize = texWidth * texHeight * sizeof(int16);
+ int16* mouseBuf = (int16*)malloc(bufferSize);
+ memset(mouseBuf, 0, bufferSize);
+
+ for (int x = 0; x < w; ++x) {
+ for (int y = 0; y < h; ++y) {
+ byte color = buf[y * w + x];
+ if (color != keycolor)
+ mouseBuf[y * texWidth + x] = _palette[color] | 0x1;
+ else
+ mouseBuf[y * texWidth + x] = 0x0;
+ }
+ }
+
+ iPhone_setMouseCursor(mouseBuf, w, h);
if (_mouseBuf != NULL && (_mouseWidth != w || _mouseHeight != h)) {
free(_mouseBuf);
diff --git a/backends/platform/linuxmoto/linuxmoto-events.cpp b/backends/platform/linuxmoto/linuxmoto-events.cpp
index 379e34b7ac..eb1bbc9394 100644
--- a/backends/platform/linuxmoto/linuxmoto-events.cpp
+++ b/backends/platform/linuxmoto/linuxmoto-events.cpp
@@ -128,7 +128,7 @@ bool OSystem_LINUXMOTO::remapKey(SDL_Event &ev, Common::Event &event) {
// VirtualKeyboard - Right Soft key
else if (ev.key.keysym.sym == SDLK_F11) {
ev.key.keysym.sym = SDLK_F7;
- }
+ }
#endif
// Joystick to Mouse
diff --git a/backends/platform/linuxmoto/linuxmoto-main.cpp b/backends/platform/linuxmoto/linuxmoto-main.cpp
index 97f12e8ce1..09b03c31d6 100644
--- a/backends/platform/linuxmoto/linuxmoto-main.cpp
+++ b/backends/platform/linuxmoto/linuxmoto-main.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/n64/Makefile b/backends/platform/n64/Makefile
index b8b2e61f77..cffe277312 100644
--- a/backends/platform/n64/Makefile
+++ b/backends/platform/n64/Makefile
@@ -15,7 +15,7 @@ AR = $(GCCN64PREFIX)ar cru
RANLIB = $(GCCN64PREFIX)ranlib
DEFINES += -D__N64__ -DLIMIT_FPS -DNONSTANDARD_PORT -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_FANCY_THEMES -DDISABLE_DOSBOX_OPL -DENABLE_VKEYBD -DUSE_ZLIB
-LIBS += -lpakfs -lframfs -ln64 -ln64utils -lromfs
+LIBS += -lpakfs -lframfs -ln64 -ln64utils -lromfs
#DEFINES += -D_ENABLE_DEBUG_
@@ -31,7 +31,7 @@ DEFINES += -DUSE_VORBIS -DUSE_TREMOR
LIBS += -lvorbisidec
endif
-LIBS += -lm -lstdc++ -lc -lgcc -lz -lnosys
+LIBS += -lm -lstdc++ -lc -lgcc -lz -lnosys
CXXFLAGS = -g -mno-extern-sdata -O2 --param max-inline-insns-auto=20 -fomit-frame-pointer -march=vr4300 -mtune=vr4300 -mhard-float -fno-rtti -fno-exceptions -Wno-multichar -Wshadow -I$(LIBN64PATH) -I$(TOOLPATH)/include -I./ -I$(srcdir) -I$(srcdir)/engines
LDFLAGS = -g -march=vr4300 -mtune=vr4300 -nodefaultlibs -nostartfiles -mno-crt0 -L$(LIBN64PATH) -L$(TOOLPATH)/lib $(LIBS) -T n64ld_cpp.x -Xlinker -Map -Xlinker scummvm.map
@@ -49,7 +49,7 @@ USE_RGB_COLOR=0
ENABLED=STATIC_PLUGIN
-ENABLE_SCUMM = $(ENABLED)
+#ENABLE_SCUMM = $(ENABLED)
#ENABLE_SCI = $(ENABLED)
#ENABLE_GOB = $(ENABLED)
#ENABLE_PARALLACTION = $(ENABLED)
@@ -74,7 +74,7 @@ all: $(TARGET).v64
$(TARGET).v64: $(TARGET).bin ROMFS.img bootcode
cat bootcode $(TARGET).bin ROMFS.img > $(TARGET).v64
- ./pad_rom.sh
+ ./pad_rom.sh $(TARGET).v64
ROMFS.img:
genromfs -f ./ROMFS.img -d ./ROMFS -V romtest
diff --git a/backends/platform/n64/README.N64 b/backends/platform/n64/README.N64
index c6ae6f021e..b47b239658 100644
--- a/backends/platform/n64/README.N64
+++ b/backends/platform/n64/README.N64
@@ -97,15 +97,15 @@ A - '.' / Skip dialogues in some games
C buttons - Numeric keypad keys
* Using a N64 Mouse:
-Used like a normal PC mouse.
+Used like a normal PC mouse.
Notes
=====
-- If virtual keyboard doesn't show up, you need to make sure you included
+- If virtual keyboard doesn't show up, you need to make sure you included
'vkeybd_default.zip' in the root of your romfs image.
-- In some games (mostly gob) cursor movement might be choppy, it's a known
+- In some games (mostly gob) cursor movement might be choppy, it's a known
problem and related on how N64 port manages screen updates.
diff --git a/backends/platform/n64/module.mk b/backends/platform/n64/module.mk
index 429b63802e..c8ceb32701 100644
--- a/backends/platform/n64/module.mk
+++ b/backends/platform/n64/module.mk
@@ -6,7 +6,7 @@ MODULE_OBJS := \
osys_n64_events.o \
osys_n64_utilities.o \
pakfs_save_manager.o \
- framfs_save_manager.o
+ framfs_save_manager.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
diff --git a/backends/platform/n64/n64.mk b/backends/platform/n64/n64.mk
new file mode 100644
index 0000000000..2e383e670d
--- /dev/null
+++ b/backends/platform/n64/n64.mk
@@ -0,0 +1,29 @@
+N64_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
+
+bundle_name = n64-dist/scummvm
+BASESIZE = 2097152
+
+all: $(N64_EXE_STRIPPED)
+
+$(N64_EXE_STRIPPED): $(EXECUTABLE)
+ $(STRIP) $< -o $@
+
+n64-distclean:
+ rm -rf $(bundle_name)
+ rm $(N64_EXE_STRIPPED)
+
+n64-dist: all
+ $(MKDIR) $(bundle_name)
+ $(MKDIR) $(bundle_name)/romfs
+ifdef DIST_FILES_ENGINEDATA
+ $(CP) $(DIST_FILES_ENGINEDATA) $(bundle_name)/romfs
+endif
+ $(CP) $(DIST_FILES_DOCS) $(bundle_name)/
+ $(CP) $(srcdir)/backends/vkeybd/packs/vkeybd_default.zip $(bundle_name)/romfs
+ genromfs -f $(bundle_name)/romfs.img -d $(bundle_name)/romfs -V scummvmn64
+ mips64-objcopy $(EXECUTABLE) $(bundle_name)/scummvm.elf -O binary
+ cat $(N64SDK)/hkz-libn64/bootcode $(bundle_name)/scummvm.elf $(bundle_name)/romfs.img > scummvm.v64
+ $(srcdir)/backends/platform/n64/pad_rom.sh scummvm.v64
+ rm scummvm.bak
+ mv scummvm.v64 $(bundle_name)/scummvm.v64
+
diff --git a/backends/platform/n64/osys_n64_base.cpp b/backends/platform/n64/osys_n64_base.cpp
index 06ff38e586..8862693138 100644
--- a/backends/platform/n64/osys_n64_base.cpp
+++ b/backends/platform/n64/osys_n64_base.cpp
@@ -859,7 +859,7 @@ void OSystem_N64::getTimeAndDate(TimeDate &t) const {
// No RTC inside the N64, read mips timer to simulate
// passing of time, not a perfect solution, but can't think
// of anything better.
-
+
uint32 now = getMilliTick();
t.tm_sec = (now / 1000) % 60;
@@ -867,7 +867,7 @@ void OSystem_N64::getTimeAndDate(TimeDate &t) const {
t.tm_hour = (((now / 1000) / 60) / 60) % 24;
t.tm_mday = 1;
t.tm_mon = 0;
- t.tm_year = 1900;
+ t.tm_year = 110;
return;
}
diff --git a/backends/platform/n64/pad_rom.sh b/backends/platform/n64/pad_rom.sh
index 0660f6c204..085203306f 100644
--- a/backends/platform/n64/pad_rom.sh
+++ b/backends/platform/n64/pad_rom.sh
@@ -1,13 +1,13 @@
#!/bin/bash
-TARGET="scummvm"
+TARGET=$1
BASESIZE=2097152
-CARTSIZE=`ls -l $TARGET.v64 | cut -d" " -f5`
+CARTSIZE=`ls -l $1 | cut -d" " -f5`
REMAINDER=`echo $CARTSIZE % $BASESIZE | bc`
REMAINDER=`echo $BASESIZE - $REMAINDER | bc`
CARTSIZE=`echo $CARTSIZE + $REMAINDER | bc`
-ucon64 -q --n64 --v64 --chk --padn=$CARTSIZE $TARGET.v64
+ucon64 -q --n64 --v64 --chk --padn=$CARTSIZE $1
diff --git a/backends/platform/openpandora/build/PXML.xml b/backends/platform/openpandora/build/PXML.xml
new file mode 100755
index 0000000000..f4d2e2a595
--- /dev/null
+++ b/backends/platform/openpandora/build/PXML.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<PXML xmlns="http://openpandora.org/namespaces/PXML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PXML_schema.xsd">
+
+ <application id="scummvm.djwillis.0001" appdata="scummvm">
+
+ <title lang="en_US">ScummVM</title>
+
+ <exec command="./runscummvm.sh"/>
+ <icon src="icon/scummvm.png"/>
+
+ <previewpics>
+ <pic src="icon/preview-pic.png"/>
+ </previewpics>
+
+ <info name="ScummVM Documentation" type="text/html" src="docs/index.html"/>
+
+ <description lang="en_US">Point & click game interpreter.</description>
+
+ <author name="DJWillis" website="http://www.scummvm.org/"/>
+
+ <version major="1" minor="1" release="1" build="1"/><!--This programs version-->
+ <osversion major="1" minor="0" release="0" build="0"/><!--The minimum OS version required-->
+
+ <categories>
+ <category name="Game"><!--category like "Games", "Graphics", "Internet" etc-->
+ <subcategory name="Adventure Games"/><!--subcategory, like "Board Games", "Strategy", "First Person Shooters"-->
+ </category>
+ </categories>
+
+ <clockspeed frequency="500"/><!--Frequency in Hz-->
+
+ </application>
+
+</PXML>
diff --git a/backends/platform/openpandora/build/README-OPENPANDORA b/backends/platform/openpandora/build/README-OPENPANDORA
new file mode 100755
index 0000000000..c8aabcbb7a
--- /dev/null
+++ b/backends/platform/openpandora/build/README-OPENPANDORA
@@ -0,0 +1,19 @@
+ScummVM - OPENPANDORA SPECIFIC README
+------------------------------------------------------------------------
+Please refer to the:
+
+ScummVM Forum: <http://forums.scummvm.org/>
+WiKi: <http://wiki.scummvm.org/index.php/OpenPandora>
+
+for the most current information on the port and any updates to this
+documentation.
+
+The wiki includes detailed instructions on how to use the port and
+control information.
+
+------------------------------------------------------------------------
+Credits
+
+Core ScummVM code (c) The ScummVM Team
+OpenPandora backend (c) John Willis
+Detailed (c) information can be found within the source code
diff --git a/backends/platform/openpandora/build/README-PND.txt b/backends/platform/openpandora/build/README-PND.txt
new file mode 100755
index 0000000000..942c3a43e2
--- /dev/null
+++ b/backends/platform/openpandora/build/README-PND.txt
@@ -0,0 +1,38 @@
+ScummVM - OPENPANDORA README - HOW TO INSTALL
+------------------------------------------------------------------------
+
+Please refer to the:
+
+ScummVM Forum: <http://forums.scummvm.org/>
+WiKi: <http://wiki.scummvm.org/index.php/OpenPandora>
+
+for the most current information on the port and any updates to this
+documentation.
+
+------------------------------------------------------------------------
+Installing:
+
+This archive contains ScummVM in a PND format ready to be copied to the
+OpenPandora and used.
+
+To install just copy the .pnd file from this archive to your device.
+
+You will need to place the .pnd file in a suitable location on your SD
+card.
+
+/pandora/desktop <- place here if you wish the icon to show on the
+ desktop. Documentation will show in the menu.
+
+/pandora/menu <- place here if you wish the icon to show on the
+ Xfce menu. Documentation will show in the menu.
+
+/pandora/apps <- place here if you wish the icon to show on the
+ desktop and in the menu. Documentation will show
+ in the menu.
+
+------------------------------------------------------------------------
+Credits
+
+Core ScummVM code (c) The ScummVM Team
+OpenPandora backend (c) John Willis
+Detailed (c) information can be found within the source code
diff --git a/backends/platform/openpandora/build/build.sh b/backends/platform/openpandora/build/build.sh
new file mode 100755
index 0000000000..10b98fe092
--- /dev/null
+++ b/backends/platform/openpandora/build/build.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+echo Quick script to make building all the time less painful.
+
+. /usr/local/angstrom/arm/environment-setup
+
+CROSS_COMPILE=arm-angstrom-linux-gnueabi-
+export CROSS_COMPILE
+
+# Export the tool names for cross-compiling
+export CXX=arm-angstrom-linux-gnueabi-g++
+export CC=arm-angstrom-linux-gnueabi-gcc
+export DEFINES=-DNDEBUG
+
+cd ../../../..
+
+echo Building ScummVM/OpenPandora.
+make
+
+echo Build for OpenPandora complete - Please check build logs.
diff --git a/backends/platform/openpandora/build/bundle.sh b/backends/platform/openpandora/build/bundle.sh
new file mode 100755
index 0000000000..12d34380bc
--- /dev/null
+++ b/backends/platform/openpandora/build/bundle.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+. /usr/local/angstrom/arm/environment-setup
+
+CROSS_COMPILE=arm-angstrom-linux-gnueabi-
+export CROSS_COMPILE
+
+echo Quick script to make building a distribution of the OpenPanodra backend more consistent.
+
+cd ../../../..
+
+make op-bundle
+make op-pnd
diff --git a/backends/platform/openpandora/build/clean.sh b/backends/platform/openpandora/build/clean.sh
new file mode 100755
index 0000000000..3574db2298
--- /dev/null
+++ b/backends/platform/openpandora/build/clean.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo Quick script to make building all the time less painful.
+
+. /usr/local/angstrom/arm/environment-setup
+
+cd ../../../..
+
+echo Cleaning ScummVM for the OpenPandora.
+make clean
diff --git a/backends/platform/openpandora/build/config-alleng.sh b/backends/platform/openpandora/build/config-alleng.sh
new file mode 100755
index 0000000000..f3fa1a0f94
--- /dev/null
+++ b/backends/platform/openpandora/build/config-alleng.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo Quick script to make running configure all the time less painful
+echo and let all the build work be done from the backend/build folder.
+
+. /usr/local/angstrom/arm/environment-setup
+
+CROSS_COMPILE=arm-angstrom-linux-gnueabi-
+export CROSS_COMPILE
+
+# Export the tool names for cross-compiling
+export CXX=arm-angstrom-linux-gnueabi-g++
+export CPPFLAGS=-I/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/include
+export LDFLAGS=-L/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/lib
+
+export DEFINES=-DNDEBUG
+
+# Edit the configure line to suit.
+cd ../../../..
+./configure --backend=openpandora --host=openpandora --disable-nasm \
+ --with-sdl-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/bin \
+ --with-mpeg2-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --disable-vorbis --enable-tremor --with-tremor-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-zlib --with-zlib-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-mad --with-mad-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-png --with-png-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-all-engines --enable-plugins --default-dynamic
+
+echo Generating config for OpenPandora complete. Check for errors.
diff --git a/backends/platform/openpandora/build/config.sh b/backends/platform/openpandora/build/config.sh
new file mode 100755
index 0000000000..9bc52a9bc4
--- /dev/null
+++ b/backends/platform/openpandora/build/config.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+echo Quick script to make running configure all the time less painful
+echo and let all the build work be done from the backend/build folder.
+
+. /usr/local/angstrom/arm/environment-setup
+
+CROSS_COMPILE=arm-angstrom-linux-gnueabi-
+export CROSS_COMPILE
+
+# Export the tool names for cross-compiling
+export CXX=arm-angstrom-linux-gnueabi-g++
+export CPPFLAGS=-I/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/include
+export LDFLAGS=-L/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/lib
+
+export DEFINES=-DNDEBUG
+
+# Edit the configure line to suit.
+cd ../../../..
+./configure --backend=openpandora --host=openpandora --disable-nasm \
+ --with-sdl-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/bin \
+ --with-mpeg2-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --disable-vorbis --enable-tremor --with-tremor-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-zlib --with-zlib-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-mad --with-mad-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-png --with-png-prefix=/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr \
+ --enable-plugins --default-dynamic
+
+echo Generating config for OpenPandora complete. Check for errors.
diff --git a/backends/platform/openpandora/build/icon/preview-pic.png b/backends/platform/openpandora/build/icon/preview-pic.png
new file mode 100755
index 0000000000..2f4a536d30
--- /dev/null
+++ b/backends/platform/openpandora/build/icon/preview-pic.png
Binary files differ
diff --git a/backends/platform/openpandora/build/icon/scummvm.png b/backends/platform/openpandora/build/icon/scummvm.png
new file mode 100755
index 0000000000..128e59efc4
--- /dev/null
+++ b/backends/platform/openpandora/build/icon/scummvm.png
Binary files differ
diff --git a/backends/platform/openpandora/build/index.html b/backends/platform/openpandora/build/index.html
new file mode 100755
index 0000000000..34d381d0f8
--- /dev/null
+++ b/backends/platform/openpandora/build/index.html
@@ -0,0 +1,26 @@
+<html>
+
+<h3>
+ <p>Welcome to the ScummVM!</p>
+</h3>
+
+<h4>
+ <p>ScummVM: OpenPandora Specific Documentation</p>
+</h4>
+
+<A href="docs/README-OPENPANDORA">ScummVM OpenPandora README</a><br/>
+<A href="http://scummvm.distant-earth.com/">ScummVM OpenPandora Website</a><br/>
+<A href="http://wiki.scummvm.org/index.php/OpenPandora">ScummVM OpenPandora WiKi</a><br/>
+
+<h4>
+ <p>ScummVM: General Documentation</p>
+</h4>
+
+<A href="http://www.scummvm.org/">ScummVM website</a><br/>
+<A href="docs/README">ScummVM README</a><br/>
+<A href="docs/NEWS">ScummVM NEWS</a><br/>
+<A href="docs/AUTHORS">ScummVM Authors</a><br/>
+<A href="docs/COPYRIGHT">ScummVM Copyright</a><br/>
+<A href="docs/COPYING">GPL Licence</a><br/>
+
+</html>
diff --git a/backends/platform/openpandora/build/pnd_make.sh b/backends/platform/openpandora/build/pnd_make.sh
new file mode 100755
index 0000000000..b19de87bb4
--- /dev/null
+++ b/backends/platform/openpandora/build/pnd_make.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+######adjust path of genpxml.sh if you want to use that "feture"#####
+
+TEMP=`getopt -o p:d:x:i:c -- "$@"`
+
+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+
+eval set -- "$TEMP"
+while true ; do
+ case "$1" in
+ -p) echo "PNDNAME set to $2" ;PNDNAME=$2;shift 2;;
+ -d) echo "FOLDER set to $2" ;FOLDER=$2;shift 2 ;;
+ -x) echo "PXML set to $2" ;PXML=$2;shift 2 ;;
+ -i) echo "ICON set to $2" ;ICON=$2;shift 2 ;;
+ -c) echo "-c set, will create compressed squasfs image instead of iso $2" ;SQUASH=1;shift 1 ;;
+ --) shift ; break ;;
+ *) echo "Error while parsing arguments! $2" ; exit 1 ;;
+ esac
+done
+
+rnd=$RANDOM; # random number for genpxml and index$rnd.xml
+
+#generate pxml if guess or empty
+if [ ! $PXML ] || [ $PXML = "guess" ] && [ $PNDNAME ] && [ $FOLDER ]; then
+ PXMLtxt=$(/home/user/libpnd/pandora-libraries/testdata/scripts/genpxml.sh $FOLDER $ICON)
+ PXML=$FOLDER/PXML.xml
+ echo "$PXMLtxt" > $FOLDER/PXML.xml
+fi
+
+#check arguments
+if [ ! $PNDNAME ] || [ ! $FOLDER ] || [ ! $PXML ]; then
+ echo " Usage: pnd_make.sh -p your.pnd -d folder/containing/your/app/ -x
+ your.pxml (or \"guess\" to try to generate it from the folder) -i icon.png"
+ exit 1
+fi
+if [ ! -d $FOLDER ]; then echo "$FOLDER doesnt exist"; exit 1; fi #check if folder actually exists
+if [ ! -f $PXML ]; then echo "$PXML doesnt exist"; exit 1; fi #check if pxml actually exists
+
+#make iso from folder
+if [ ! $SQUASH ]; then
+ mkisofs -o $PNDNAME.iso -R $FOLDER
+else
+ if [ $(mksquashfs -version | awk '{if ($3 >= 4) print 1}') = 1 ]; then
+ echo "your squashfs version is older then version 4, please upgrade to 4.0 or later"
+ exit 1
+ fi
+ mksquashfs -no-recovery -nopad $FOLDER $PNDNAME.iso
+fi
+#append pxml to iso
+cat $PNDNAME.iso $PXML > $PNDNAME
+rm $PNDNAME.iso #cleanup
+
+#append icon if specified
+if [ $ICON ]; then # check if we want to add an icon
+ if [ ! -f $ICON ]; then #does the icon actually exist?
+ echo "$ICON doesnt exist"
+ else # yes
+ mv $PNDNAME $PNDNAME.tmp
+ cat $PNDNAME.tmp $ICON > $PNDNAME # append icon
+ rm $PNDNAME.tmp #cleanup
+ fi
+fi
+
+if [ $PXML = "guess" ];then rm $FOLDER/PXML.xml; fi #cleanup
diff --git a/backends/platform/openpandora/build/runscummvm.sh b/backends/platform/openpandora/build/runscummvm.sh
new file mode 100755
index 0000000000..48d24a2b81
--- /dev/null
+++ b/backends/platform/openpandora/build/runscummvm.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Make sure any extra libs not in the firmware are pulled in.
+LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../lib
+export LD_LIBRARY_PATH
+
+# Ensure we have a folder to store save games on the SD card.
+mkdir saves
+
+# make a runtime dir, just incase it creates anything in CWD
+mkdir runtime
+cd runtime
+
+../bin/scummvm --fullscreen --gfx-mode=2x --config=../scummvm.config
diff --git a/backends/platform/openpandora/module.mk b/backends/platform/openpandora/module.mk
new file mode 100755
index 0000000000..1e5f6bcd69
--- /dev/null
+++ b/backends/platform/openpandora/module.mk
@@ -0,0 +1,16 @@
+MODULE := backends/platform/openpandora
+
+MODULE_OBJS := \
+ op-graphics.o \
+ op-events.o \
+ op-options.o \
+ op-main.o
+
+MODULE_DIRS += \
+ backends/platform/openpandora/
+
+# We don't use the rules.mk here on purpose
+OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS)
+
+# Hack to ensure the SDL backend is built so we can use OSystem_SDL.
+-include $(srcdir)/backends/platform/sdl/module.mk
diff --git a/backends/platform/openpandora/op-bundle.mk b/backends/platform/openpandora/op-bundle.mk
new file mode 100755
index 0000000000..163f4711ce
--- /dev/null
+++ b/backends/platform/openpandora/op-bundle.mk
@@ -0,0 +1,85 @@
+# Special target to create bundles and PND's for the OpenPandora.
+
+#bundle_name = release/scummvm-op-`date '+%Y-%m-%d'`
+bundle_name = release/scummvm-op
+f=$(shell which $(STRIP))
+libloc = $(shell dirname $(f))
+
+op-bundle: $(EXECUTABLE)
+ $(MKDIR) "$(bundle_name)"
+ $(MKDIR) "$(bundle_name)/scummvm"
+ $(MKDIR) "$(bundle_name)/scummvm/bin"
+ $(MKDIR) "$(bundle_name)/scummvm/data"
+ $(MKDIR) "$(bundle_name)/scummvm/docs"
+ $(MKDIR) "$(bundle_name)/scummvm/icon"
+ $(MKDIR) "$(bundle_name)/scummvm/lib"
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/runscummvm.sh $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/PXML.xml $(bundle_name)/scummvm/data/
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/icon/scummvm.png $(bundle_name)/scummvm/icon/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/icon/preview-pic.png $(bundle_name)/scummvm/icon/
+
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/README-OPENPANDORA $(bundle_name)/scummvm/docs/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/index.html $(bundle_name)/scummvm/docs/
+
+ $(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/docs/
+
+ $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/data/
+ $(INSTALL) -c -m 644 $(DIST_FILES_ENGINEDATA) $(bundle_name)/scummvm/data/
+
+ $(STRIP) $(EXECUTABLE) -o $(bundle_name)/scummvm/bin/$(EXECUTABLE)
+
+ifdef DYNAMIC_MODULES
+ $(INSTALL) -d "$(bundle_name)/scummvm/plugins"
+ $(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
+ $(STRIP) $(bundle_name)/scummvm/plugins/*
+endif
+
+ $(CP) $(libloc)/../arm-angstrom-linux-gnueabi/usr/lib/libFLAC.so.8.2.0 $(bundle_name)/scummvm/lib/libFLAC.so.8
+ tar -C $(bundle_name) -cvjf $(bundle_name).tar.bz2 .
+ rm -R ./$(bundle_name)
+
+op-pnd: $(EXECUTABLE)
+ $(MKDIR) "$(bundle_name)"
+ $(MKDIR) "$(bundle_name)/scummvm"
+ $(MKDIR) "$(bundle_name)/scummvm/bin"
+ $(MKDIR) "$(bundle_name)/scummvm/data"
+ $(MKDIR) "$(bundle_name)/scummvm/docs"
+ $(MKDIR) "$(bundle_name)/scummvm/icon"
+ $(MKDIR) "$(bundle_name)/scummvm/lib"
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/runscummvm.sh $(bundle_name)/scummvm/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/PXML.xml $(bundle_name)/scummvm/data/
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/icon/scummvm.png $(bundle_name)/scummvm/icon/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/icon/preview-pic.png $(bundle_name)/scummvm/icon/
+
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/README-OPENPANDORA $(bundle_name)/scummvm/docs/
+ $(CP) $(srcdir)/backends/platform/openpandora/build/index.html $(bundle_name)/scummvm/docs/
+
+ $(INSTALL) -c -m 644 $(DIST_FILES_DOCS) $(bundle_name)/scummvm/docs/
+
+ $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(bundle_name)/scummvm/data/
+ $(INSTALL) -c -m 644 $(DIST_FILES_ENGINEDATA) $(bundle_name)/scummvm/data/
+
+ $(STRIP) $(EXECUTABLE) -o $(bundle_name)/scummvm/bin/$(EXECUTABLE)
+
+ifdef DYNAMIC_MODULES
+ $(INSTALL) -d "$(bundle_name)/scummvm/plugins"
+ $(INSTALL) -c -m 644 $(PLUGINS) "$(bundle_name)/scummvm/plugins"
+ $(STRIP) $(bundle_name)/scummvm/plugins/*
+endif
+
+ $(CP) $(libloc)/../arm-angstrom-linux-gnueabi/usr/lib/libFLAC.so.8.2.0 $(bundle_name)/scummvm/lib/libFLAC.so.8
+
+ $(srcdir)/backends/platform/openpandora/build/pnd_make.sh -p $(bundle_name).pnd -d $(bundle_name)/scummvm -x $(bundle_name)/scummvm/data/PXML.xml -i $(bundle_name)/scummvm/icon/scummvm.png
+
+ $(CP) $(srcdir)/backends/platform/openpandora/build/README-PND.txt $(bundle_name)
+ tar -cvjf $(bundle_name)-pnd.tar.bz2 $(bundle_name).pnd $(bundle_name)/README-PND.txt
+ rm -R ./$(bundle_name)
+# rm $(bundle_name).pnd
+
+.PHONY: op-bundle op-pnd
diff --git a/backends/platform/openpandora/op-events.cpp b/backends/platform/openpandora/op-events.cpp
new file mode 100755
index 0000000000..24283aa8ba
--- /dev/null
+++ b/backends/platform/openpandora/op-events.cpp
@@ -0,0 +1,176 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * OpenPandora: Device Specific Event Handling.
+ *
+ */
+
+#include "backends/platform/openpandora/op-sdl.h"
+#include "backends/platform/openpandora/op-options.h"
+
+/* Quick default button states for modifiers. */
+int BUTTON_STATE_L = false;
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
+};
+
+/* On the OpenPandora by default the ABXY and L/R Trigger buttons are returned by SDL as
+ (A): SDLK_HOME (B): SDLK_END (X): SDLK_PAGEDOWN (Y): SDLK_PAGEUP (L): SDLK_RSHIFT (R): SDLK_RCTRL
+*/
+
+bool OSystem_OP::handleKeyDown(SDL_Event &ev, Common::Event &event) {
+ switch (ev.key.keysym.sym) {
+ case SDLK_HOME:
+ event.type = Common::EVENT_LBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+ case SDLK_END:
+ event.type = Common::EVENT_RBUTTONDOWN;
+ fillMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+ case SDLK_PAGEDOWN:
+ event.type = Common::EVENT_MAINMENU;
+ return true;
+ break;
+ case SDLK_PAGEUP:
+ OP::ToggleTapMode();
+ if (OP::tapmodeLevel == TAPMODE_LEFT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Left Click");
+ } else if (OP::tapmodeLevel == TAPMODE_RIGHT) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Right Click");
+ } else if (OP::tapmodeLevel == TAPMODE_HOVER) {
+ displayMessageOnOSD("Touchscreen 'Tap Mode' - Hover (No Click)");
+ }
+ break;
+ case SDLK_RSHIFT:
+ BUTTON_STATE_L = true;
+ break;
+ case SDLK_RCTRL:
+ break;
+ default:
+ return OSystem_SDL::handleKeyDown(ev, event);
+ break;
+ }
+ return false;
+}
+
+bool OSystem_OP::handleKeyUp(SDL_Event &ev, Common::Event &event) {
+ switch (ev.key.keysym.sym) {
+ case SDLK_HOME:
+ event.type = Common::EVENT_LBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+ case SDLK_END:
+ event.type = Common::EVENT_RBUTTONUP;
+ fillMouseEvent(event, _km.x, _km.y);
+ return true;
+ break;
+ case SDLK_PAGEDOWN:
+ event.type = Common::EVENT_MAINMENU;
+ return true;
+ break;
+ case SDLK_PAGEUP:
+ break;
+ case SDLK_RSHIFT:
+ BUTTON_STATE_L = false;
+ break;
+ case SDLK_RCTRL:
+ break;
+ default:
+ return OSystem_SDL::handleKeyUp(ev, event);
+ break;
+ }
+ return false;
+}
+
+/* Custom handleMouseButtonDown/handleMouseButtonUp to deal with 'Tap Mode' for the touchscreen */
+
+bool OSystem_OP::handleMouseButtonDown(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (OP::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONDOWN;
+ else if (OP::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONDOWN;
+ else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONDOWN; /* For normal mice etc. */
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONDOWN;
+#if defined(SDL_BUTTON_WHEELUP) && defined(SDL_BUTTON_WHEELDOWN)
+ else if (ev.button.button == SDL_BUTTON_WHEELUP)
+ event.type = Common::EVENT_WHEELUP;
+ else if (ev.button.button == SDL_BUTTON_WHEELDOWN)
+ event.type = Common::EVENT_WHEELDOWN;
+#endif
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONDOWN;
+#endif
+ else
+ return false;
+
+ fillMouseEvent(event, ev.button.x, ev.button.y);
+
+ return true;
+}
+
+bool OSystem_OP::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) {
+ if (ev.button.button == SDL_BUTTON_LEFT){
+ if (BUTTON_STATE_L == true) /* BUTTON_STATE_L = Left Trigger Held, force Right Click */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (OP::tapmodeLevel == TAPMODE_LEFT) /* TAPMODE_LEFT = Left Click Tap Mode */
+ event.type = Common::EVENT_LBUTTONUP;
+ else if (OP::tapmodeLevel == TAPMODE_RIGHT) /* TAPMODE_RIGHT = Right Click Tap Mode */
+ event.type = Common::EVENT_RBUTTONUP;
+ else if (OP::tapmodeLevel == TAPMODE_HOVER) /* TAPMODE_HOVER = Hover (No Click) Tap Mode */
+ event.type = Common::EVENT_MOUSEMOVE;
+ else
+ event.type = Common::EVENT_LBUTTONUP; /* For normal mice etc. */
+ }
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ event.type = Common::EVENT_RBUTTONUP;
+#if defined(SDL_BUTTON_MIDDLE)
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ event.type = Common::EVENT_MBUTTONUP;
+#endif
+ else
+ return false;
+
+ fillMouseEvent(event, ev.button.x, ev.button.y);
+
+ return true;
+}
diff --git a/backends/platform/openpandora/op-graphics.cpp b/backends/platform/openpandora/op-graphics.cpp
new file mode 100755
index 0000000000..ef95f52e99
--- /dev/null
+++ b/backends/platform/openpandora/op-graphics.cpp
@@ -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$
+ *
+ */
+
+#include "backends/platform/openpandora/op-sdl.h"
+#include "common/mutex.h"
+#include "common/translation.h"
+#include "common/util.h"
+
+#include "graphics/scaler/aspect.h"
+#include "graphics/surface.h"
+
+bool OSystem_OP::loadGFXMode() {
+ /* FIXME: For now we just cheat and set the overlay to 640*480 not 800*480 and let SDL
+ deal with the boarders (it saves cleaning up the overlay when the game screen is
+ smaller than the overlay ;)
+ */
+ _videoMode.overlayWidth = 640;
+ _videoMode.overlayHeight = 480;
+ _videoMode.fullscreen = true;
+
+ if (_videoMode.screenHeight != 200 && _videoMode.screenHeight != 400)
+ _videoMode.aspectRatioCorrection = false;
+
+ OSystem_SDL::loadGFXMode();
+
+ return true;
+
+}
diff --git a/backends/platform/openpandora/op-main.cpp b/backends/platform/openpandora/op-main.cpp
new file mode 100755
index 0000000000..4febd404c3
--- /dev/null
+++ b/backends/platform/openpandora/op-main.cpp
@@ -0,0 +1,265 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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/scummsys.h"
+#include <SDL/SDL.h>
+
+#include "backends/platform/openpandora/op-sdl.h"
+#include "backends/plugins/posix/posix-provider.h"
+#include "base/main.h"
+
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/events.h"
+#include "common/util.h"
+
+#include "common/file.h"
+#include "base/main.h"
+
+#include "backends/saves/default/default-saves.h"
+
+#include "backends/timer/default/default-timer.h"
+#include "sound/mixer_intern.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h> // for getTimeAndDate()
+
+/* Dump console info to files. */
+#define DUMP_STDOUT
+
+static SDL_Cursor *hiddenCursor;
+
+int main(int argc, char *argv[]) {
+ g_system = new OSystem_OP();
+ assert(g_system);
+
+#ifdef DYNAMIC_MODULES
+ PluginManager::instance().addPluginProvider(new POSIXPluginProvider());
+#endif
+
+ // Invoke the actual ScummVM main entry point:
+ int res = scummvm_main(argc, argv);
+ g_system->quit();
+
+ return res;
+}
+
+static Uint32 timer_handler(Uint32 interval, void *param) {
+ ((DefaultTimerManager *)param)->handler();
+ return interval;
+}
+
+void OSystem_OP::initBackend() {
+
+ assert(!_inited);
+
+ uint8_t hiddenCursorData = 0;
+
+ int joystick_num = ConfMan.getInt("joystick_num");
+ uint32 sdlFlags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
+
+ if (ConfMan.hasKey("disable_sdl_parachute"))
+ sdlFlags |= SDL_INIT_NOPARACHUTE;
+
+ if (joystick_num > -1)
+ sdlFlags |= SDL_INIT_JOYSTICK;
+
+ if (SDL_Init(sdlFlags) == -1) {
+ error("Could not initialize SDL: %s", SDL_GetError());
+ }
+
+ hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0);
+
+ /* Setup default save path to be workingdir/saves */
+
+ char savePath[PATH_MAX+1];
+ char workDirName[PATH_MAX+1];
+
+ if (getcwd(workDirName, PATH_MAX) == NULL) {
+ error("Could not obtain current working directory.");
+ } else {
+ printf("Current working directory: %s\n", workDirName);
+ }
+
+ strcpy(savePath, workDirName);
+ strcat(savePath, "/../saves");
+ printf("Current save directory: %s\n", savePath);
+ struct stat sb;
+ if (stat(savePath, &sb) == -1)
+ if (errno == ENOENT) // Create the dir if it does not exist
+ if (mkdir(savePath, 0755) != 0)
+ warning("mkdir for '%s' failed!", savePath);
+
+ _savefile = new DefaultSaveFileManager(savePath);
+
+ #ifdef DUMP_STDOUT
+ // The OpenPandora has a serial console on the EXT connection but most users do not use this so we
+ // output all our STDOUT and STDERR to files for debug purposes.
+ char STDOUT_FILE[PATH_MAX+1];
+ char STDERR_FILE[PATH_MAX+1];
+
+ strcpy(STDOUT_FILE, workDirName);
+ strcpy(STDERR_FILE, workDirName);
+ strcat(STDOUT_FILE, "/scummvm.stdout.txt");
+ strcat(STDERR_FILE, "/scummvm.stderr.txt");
+
+ // Flush the output in case anything is queued
+ fclose(stdout);
+ fclose(stderr);
+
+ // Redirect standard input and standard output
+ FILE *newfp = freopen(STDOUT_FILE, "w", stdout);
+ if (newfp == NULL) {
+ #if !defined(stdout)
+ stdout = fopen(STDOUT_FILE, "w");
+ #else
+ newfp = fopen(STDOUT_FILE, "w");
+ if (newfp) {
+ *stdout = *newfp;
+ }
+ #endif
+ }
+
+ newfp = freopen(STDERR_FILE, "w", stderr);
+ if (newfp == NULL) {
+ #if !defined(stderr)
+ stderr = fopen(STDERR_FILE, "w");
+ #else
+ newfp = fopen(STDERR_FILE, "w");
+ if (newfp) {
+ *stderr = *newfp;
+ }
+ #endif
+ }
+
+ setbuf(stderr, NULL);
+ printf("%s\n", "Debug: STDOUT and STDERR redirected to text files.");
+ #endif /* DUMP_STDOUT */
+
+ /* Trigger autosave every 4 minutes. */
+ ConfMan.registerDefault("autosave_period", 4 * 60);
+
+ ConfMan.registerDefault("fullscreen", true);
+
+ /* Make sure that aspect ratio correction is enabled on the 1st run to stop
+ users asking me what the 'wasted space' at the bottom is ;-). */
+ ConfMan.registerDefault("aspect_ratio", true);
+
+ /* Make sure SDL knows that we have a joystick we want to use. */
+ ConfMan.setInt("joystick_num", 0);
+
+ _graphicsMutex = createMutex();
+
+ /* On the OpenPandora we need to work around an SDL assumption that
+ returns relative mouse coordinates when you get to the screen
+ edges using the touchscreen. The workaround is to set a blank
+ SDL cursor and not disable it (Hackish I know).
+
+ The root issues likes in the Windows Manager GRAB code in SDL.
+ That is why the issue is not seen on framebuffer devices like the
+ GP2X (there is no X window manager ;)).
+ */
+ SDL_ShowCursor(SDL_ENABLE);
+ SDL_SetCursor(hiddenCursor);
+
+ // Enable unicode support if possible
+ SDL_EnableUNICODE(1);
+
+ memset(&_oldVideoMode, 0, sizeof(_oldVideoMode));
+ memset(&_videoMode, 0, sizeof(_videoMode));
+ memset(&_transactionDetails, 0, sizeof(_transactionDetails));
+
+ _videoMode.mode = GFX_DOUBLESIZE;
+ _videoMode.scaleFactor = 2;
+ _videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio");
+ _scalerProc = Normal2x;
+ _scalerType = 0;
+
+ _videoMode.fullscreen = true;
+
+ // enable joystick
+ if (joystick_num > -1 && SDL_NumJoysticks() > 0) {
+ printf("Using joystick: %s\n", SDL_JoystickName(0));
+ _joystick = SDL_JoystickOpen(joystick_num);
+ }
+
+ setupMixer();
+
+ // Note: We could implement a custom SDLTimerManager by using
+ // SDL_AddTimer. That might yield better timer resolution, but it would
+ // also change the semantics of a timer: Right now, ScummVM timers
+ // *never* run in parallel, due to the way they are implemented. If we
+ // switched to SDL_AddTimer, each timer might run in a separate thread.
+ // However, not all our code is prepared for that, so we can't just
+ // switch. Still, it's a potential future change to keep in mind.
+ _timer = new DefaultTimerManager();
+ _timerID = SDL_AddTimer(10, &timer_handler, _timer);
+
+ // Invoke parent implementation of this method
+ OSystem::initBackend();
+
+ _inited = true;
+}
+
+void OSystem_OP::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+
+ /* Setup default extra data paths for engine data files and plugins */
+
+ char workDirName[PATH_MAX+1];
+
+ if (getcwd(workDirName, PATH_MAX) == NULL) {
+ error("Error: Could not obtain current working directory.");
+ }
+
+ char enginedataPath[PATH_MAX+1];
+
+ strcpy(enginedataPath, workDirName);
+ strcat(enginedataPath, "/../data");
+ printf("Default engine data directory: %s\n", enginedataPath);
+
+ Common::FSNode engineNode(enginedataPath);
+ if (engineNode.exists() && engineNode.isDirectory()) {
+ s.add("__OP_ENGDATA__", new Common::FSDirectory(enginedataPath), priority);
+ }
+}
+
+void OSystem_OP::quit() {
+
+ SDL_FreeCursor(hiddenCursor);
+
+ #ifdef DUMP_STDOUT
+ printf("%s\n", "Debug: STDOUT and STDERR text files closed.");
+ fclose(stdout);
+ fclose(stderr);
+ #endif /* DUMP_STDOUT */
+
+ OSystem_SDL::quit();
+}
diff --git a/backends/platform/openpandora/op-options.cpp b/backends/platform/openpandora/op-options.cpp
new file mode 100755
index 0000000000..f8711b868a
--- /dev/null
+++ b/backends/platform/openpandora/op-options.cpp
@@ -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$
+ *
+ */
+
+/*
+ * OpenPandora: Options, custom code and hardware stuff.
+ *
+ */
+
+#include "backends/platform/openpandora/op-options.h"
+
+namespace OP {
+
+enum {
+ /* Touchscreen TapMode */
+ TAPMODE_LEFT = 0,
+ TAPMODE_RIGHT = 1,
+ TAPMODE_HOVER = 2
+};
+
+int tapmodeLevel = TAPMODE_LEFT;
+
+void ToggleTapMode() {
+ if (tapmodeLevel == TAPMODE_LEFT) {
+ tapmodeLevel = TAPMODE_RIGHT;
+ } else if (tapmodeLevel == TAPMODE_RIGHT) {
+ tapmodeLevel = TAPMODE_HOVER;
+ } else if (tapmodeLevel == TAPMODE_HOVER) {
+ tapmodeLevel = TAPMODE_LEFT;
+ } else {
+ tapmodeLevel = TAPMODE_LEFT;
+ }
+}
+
+} /* namespace OP */
diff --git a/engines/gob/helper.h b/backends/platform/openpandora/op-options.h
index d1f24792a5..8c2bb1cc89 100644..100755
--- a/engines/gob/helper.h
+++ b/backends/platform/openpandora/op-options.h
@@ -23,18 +23,20 @@
*
*/
-#ifndef GOB_HELPER_H
-#define GOB_HELPER_H
+/*
+ * OpenPandora: Options, custom code and hardware stuff.
+ *
+ */
+
+#ifndef OP_OPTIONS_H
+#define OP_OPTIONS_H
+
+namespace OP {
-namespace Gob {
+extern int tapmodeLevel;
-/** A strncpy that forces the final \0. */
-inline char *strncpy0(char *dest, const char *src, size_t n) {
- strncpy(dest, src, n);
- dest[n] = 0;
- return dest;
-}
+extern void ToggleTapMode();
-} // End of namespace Gob
+} /* namespace OP */
-#endif // GOB_HELPER_H
+#endif //OP_OPTIONS_H
diff --git a/backends/platform/openpandora/op-sdl.h b/backends/platform/openpandora/op-sdl.h
new file mode 100755
index 0000000000..8561b42498
--- /dev/null
+++ b/backends/platform/openpandora/op-sdl.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$
+ *
+ */
+
+#ifndef OP_SDL_H
+#define OP_SDL_H
+
+#include "backends/platform/sdl/sdl.h"
+
+#define __OPENPANDORA__
+#define MIXER_DOUBLE_BUFFERING 1
+
+#ifndef PATH_MAX
+ #define PATH_MAX 255
+#endif
+
+class OSystem_OP : public OSystem_SDL {
+public:
+ OSystem_OP() {}
+
+ /* Events */
+ bool handleKeyDown(SDL_Event &ev, Common::Event &event);
+ bool handleKeyUp(SDL_Event &ev, Common::Event &event);
+ bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event);
+ bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event);
+
+ /* Graphics */
+ bool loadGFXMode();
+
+ /* Platform Setup Stuff */
+ void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
+ void initBackend();
+ void quit();
+
+protected:
+
+};
+#endif
diff --git a/backends/platform/ps2/Makefile.gdb b/backends/platform/ps2/Makefile.gdb
index 53646a9546..48dcebc1d4 100644
--- a/backends/platform/ps2/Makefile.gdb
+++ b/backends/platform/ps2/Makefile.gdb
@@ -51,7 +51,7 @@ INCDIR = ../../../
DEFINES = -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -D_EE -D__PLAYSTATION2__ -D__PS2_DEBUG__ -g -Wall -Wno-multichar
-INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS))
+INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS))
INCLUDES += -I $(PS2GDB)/ee -I $(PS2SDK)/ee/include -I $(PS2SDK)/common/include -I ./common -I . -I $(srcdir) -I $(srcdir)/engines
TARGET = elf/scummvm.elf
@@ -72,16 +72,16 @@ OBJS := backends/platform/ps2/DmaPipe.o \
backends/platform/ps2/ps2mutex.o \
backends/platform/ps2/ps2time.o \
backends/platform/ps2/ps2debug.o
-
+
MODULE_DIRS += .
BACKEND := ps2
include $(srcdir)/Makefile.common
-LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(PS2SDK)/ee/startup/linkfile
+LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(PS2SDK)/ee/startup/linkfile
LDFLAGS += -L $(PS2GDB)/lib -L $(PS2SDK)/ee/lib -L .
-LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS))
+LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS))
LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lps2gdbStub -lps2ip -ldebug -lkernel -lstdc++
all: $(TARGET)
diff --git a/backends/platform/ps2/Makefile.ps2 b/backends/platform/ps2/Makefile.ps2
index 8c81341253..472ba5ec3a 100644
--- a/backends/platform/ps2/Makefile.ps2
+++ b/backends/platform/ps2/Makefile.ps2
@@ -1,45 +1,22 @@
# $Header: Exp $
include $(PS2SDK)/Defs.make
-PS2_EXTRA = /home/tony/GSOC/ps2/sdk-extra
+PS2_EXTRA = /works/devel/ps2/sdk-extra
PS2_EXTRA_INCS = /zlib/include /libmad/ee/include /SjPcm/ee/src /tremor
PS2_EXTRA_LIBS = /zlib/lib /libmad/ee/lib /SjPcm/ee/lib /tremor/tremor
-# Set to 1 to enable, 0 to disable dynamic modules
-DYNAMIC_MODULES = 1
-# Set to 1 to enable, 0 to disable more detailed printing of gcc commands
-VERBOSE_BUILD=0
-
-# Test for dynamic plugins
-ifeq ($(DYNAMIC_MODULES),1)
-ENABLED = DYNAMIC_PLUGIN
-DEFINES = -DDYNAMIC_MODULES
-PRE_OBJS_FLAGS = -Wl,--whole-archive
-POST_OBJS_FLAGS = -Wl,--no-whole-archive
-else
-ENABLED = STATIC_PLUGIN
-endif
-
-#control build
-DISABLE_SCALERS = true
-DISABLE_HQ_SCALERS = true
+ENABLED=STATIC_PLUGIN
ENABLE_SCUMM = $(ENABLED)
ENABLE_SCUMM_7_8 = $(ENABLED)
ENABLE_HE = $(ENABLED)
ENABLE_AGI = $(ENABLED)
ENABLE_AGOS = $(ENABLED)
-ENABLE_AGOS2 = $(ENABLED)
ENABLE_CINE = $(ENABLED)
ENABLE_CRUISE = $(ENABLED)
-ENABLE_DRACI = $(ENABLED)
ENABLE_DRASCULA = $(ENABLED)
ENABLE_GOB = $(ENABLED)
-ENABLE_GROOVIE = $(ENABLED)
-# ENABLE_GROOVIE2 = $(ENABLED)
-ENABLE_IHNM = $(ENABLED)
ENABLE_KYRA = $(ENABLED)
-# ENABLE_LOL = $(ENABLED)
ENABLE_LURE = $(ENABLED)
# ENABLE_M4 = $(ENABLED)
ENABLE_MADE = $(ENABLED)
@@ -47,18 +24,16 @@ ENABLE_PARALLACTION = $(ENABLED)
ENABLE_QUEEN = $(ENABLED)
ENABLE_SAGA = $(ENABLED)
ENABLE_SAGA2 = $(ENABLED)
-ENABLE_SCI = $(ENABLED)
-# ENABLE_SCI32 = $(ENABLED)
+ENABLE_IHNM = $(ENABLED)
ENABLE_SKY = $(ENABLED)
ENABLE_SWORD1 = $(ENABLED)
ENABLE_SWORD2 = $(ENABLED)
-ENABLE_TEENAGENT = $(ENABLED)
-ENABLE_TINSEL = $(ENABLED)
+# ENABLE_TINSEL = $(ENABLED)
ENABLE_TOUCHE = $(ENABLED)
-ENABLE_TUCKER = $(ENABLED)
HAVE_GCC3 = true
-CC = ee-gcc
+
+CC = ee-gcc
CXX = ee-g++
AS = ee-gcc
LD = ee-gcc
@@ -67,60 +42,51 @@ RANLIB = ee-ranlib
STRIP = ee-strip
MKDIR = mkdir -p
RM = rm -f
-RM_REC = rm -rf
-MODULE_DIRS = ./
srcdir = ../../..
VPATH = $(srcdir)
INCDIR = ../../../
-DEPDIR = .deps
+# DEPDIR = .deps
-TARGET = elf/scummvm.elf
+DEFINES = -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -D_EE -D__PLAYSTATION2__ -O2 -Wall -Wno-multichar
-DEFINES += -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -DDISABLE_SAVEGAME_SORTING -D_EE -D__PLAYSTATION2__ -G2 -O2 -Wall -Wno-multichar -fno-rtti -fno-exceptions -DNO_ADAPTOR
-DEFINES += -DUSE_ELF_LOADER -DMIPS_TARGET -DONE_PLUGIN_AT_A_TIME
-INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS))
+INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS))
INCLUDES += -I $(PS2SDK)/ee/include -I $(PS2SDK)/common/include -I ./common -I . -I $(srcdir) -I $(srcdir)/engines
-CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP
-
-# Variables for dynamic plugin building
-PLUGIN_PREFIX =
-PLUGIN_SUFFIX = .plg
-PLUGIN_EXTRA_DEPS = $(srcdir)/backends/plugins/elf/plugin.syms elf/scummvm.elf
-PLUGIN_LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o
-PLUGIN_LDFLAGS += -nostartfiles -Wl,-q,--just-symbols,elf/scummvm.elf,-T$(srcdir)/backends/plugins/ps2/plugin.ld,--retain-symbols-file,$(srcdir)/backends/plugins/elf/plugin.syms -lstdc++ -lc
+TARGET = elf/scummvm.elf
+OBJS := backends/platform/ps2/DmaPipe.o \
+ backends/platform/ps2/Gs2dScreen.o \
+ backends/platform/ps2/irxboot.o \
+ backends/platform/ps2/ps2input.o \
+ backends/platform/ps2/ps2pad.o \
+ backends/platform/ps2/savefilemgr.o \
+ backends/platform/ps2/fileio.o \
+ backends/platform/ps2/asyncfio.o \
+ backends/platform/ps2/icon.o \
+ backends/platform/ps2/cd.o \
+ backends/platform/ps2/eecodyvdfs.o \
+ backends/platform/ps2/rpckbd.o \
+ backends/platform/ps2/systemps2.o \
+ backends/platform/ps2/ps2mutex.o \
+ backends/platform/ps2/ps2time.o \
+ backends/platform/ps2/ps2debug.o
+
+MODULE_DIRS += .
BACKEND := ps2
-LDFLAGS = -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(srcdir)/backends/plugins/ps2/main_prog.ld
+include $(srcdir)/Makefile.common
+
+LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(PS2SDK)/ee/startup/linkfile
LDFLAGS += -L $(PS2SDK)/ee/lib -L .
LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS))
-LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lkernel -lstdc++
-
-OBJS := $(srcdir)/backends/platform/ps2/DmaPipe.o \
- $(srcdir)/backends/platform/ps2/Gs2dScreen.o \
- $(srcdir)/backends/platform/ps2/irxboot.o \
- $(srcdir)/backends/platform/ps2/ps2input.o \
- $(srcdir)/backends/platform/ps2/ps2pad.o \
- $(srcdir)/backends/platform/ps2/savefilemgr.o \
- $(srcdir)/backends/platform/ps2/fileio.o \
- $(srcdir)/backends/platform/ps2/asyncfio.o \
- $(srcdir)/backends/platform/ps2/icon.o \
- $(srcdir)/backends/platform/ps2/cd.o \
- $(srcdir)/backends/platform/ps2/eecodyvdfs.o \
- $(srcdir)/backends/platform/ps2/rpckbd.o \
- $(srcdir)/backends/platform/ps2/systemps2.o \
- $(srcdir)/backends/platform/ps2/ps2mutex.o \
- $(srcdir)/backends/platform/ps2/ps2time.o \
- $(srcdir)/backends/platform/ps2/ps2debug.o
-
-include $(srcdir)/Makefile.common
+LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lkernel -lstdc++
+LDFLAGS += -s
all: $(TARGET)
$(TARGET): $(OBJS)
- $(LD) $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LDFLAGS) -o $@
+ $(LD) $^ $(LDFLAGS) -o $@
diff --git a/backends/platform/ps2/fileio.cpp b/backends/platform/ps2/fileio.cpp
index 8c10156aaf..826a2578e4 100644
--- a/backends/platform/ps2/fileio.cpp
+++ b/backends/platform/ps2/fileio.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "backends/platform/ps2/fileio.h"
#include <tamtypes.h>
diff --git a/backends/platform/ps2/iop/CoDyVDfs/iop/imports.lst b/backends/platform/ps2/iop/CoDyVDfs/iop/imports.lst
index d3ed3b0442..eb85e04462 100644
--- a/backends/platform/ps2/iop/CoDyVDfs/iop/imports.lst
+++ b/backends/platform/ps2/iop/CoDyVDfs/iop/imports.lst
@@ -24,10 +24,10 @@ I_DelDrv
iomanX_IMPORTS_end
sifcmd_IMPORTS_start
-I_sceSifInitRpc
-I_sceSifSetRpcQueue
-I_sceSifRegisterRpc
-I_sceSifRpcLoop
+I_sceSifInitRpc
+I_sceSifSetRpcQueue
+I_sceSifRegisterRpc
+I_sceSifRpcLoop
sifcmd_IMPORTS_end
stdio_IMPORTS_start
@@ -59,7 +59,7 @@ thbase_IMPORTS_start
I_CreateThread
I_StartThread
I_GetThreadId
-I_DelayThread
+I_DelayThread
thbase_IMPORTS_end
diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp
index 357404c5c4..d525a35b7d 100644
--- a/backends/platform/ps2/systemps2.cpp
+++ b/backends/platform/ps2/systemps2.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include <kernel.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile
index e564200897..9f6b5c8921 100644
--- a/backends/platform/psp/Makefile
+++ b/backends/platform/psp/Makefile
@@ -126,7 +126,7 @@ endif
# PSP LIBS
PSPLIBS = -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk \
-lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspaudiocodec \
- -lpspkernel
+ -lpspkernel -lpspnet_inet
# Add in PSPSDK includes and libraries.
@@ -148,7 +148,10 @@ OBJS := powerman.o \
thread.o \
rtc.o \
mp3.o \
- tests.o
+ png_loader.o \
+ image_viewer.o \
+ tests.o \
+ dummy.o
BACKEND := psp
@@ -200,4 +203,4 @@ SCEkxploit: $(TARGET).elf $(PSP_EBOOT_SFO)
$(PACK_PBP) "%__SCE__$(TARGET)/$(PSP_EBOOT)" $(PSP_EBOOT_SFO) $(PSP_EBOOT_ICON) \
$(PSP_EBOOT_ICON1) $(PSP_EBOOT_PIC0) $(PSP_EBOOT_PIC1) \
$(PSP_EBOOT_SND0) NULL $(PSP_EBOOT_PSAR)
-
+
diff --git a/backends/platform/psp/README.PSP b/backends/platform/psp/README.PSP
index f4d5bae6d6..b83f1cab6d 100644
--- a/backends/platform/psp/README.PSP
+++ b/backends/platform/psp/README.PSP
@@ -1,81 +1,130 @@
-ScummVM-PSP 1.2.0svn README
+ScummVM-PSP 1.3.0svn README
==============================================================================
Installation
============
- - Copy the relevant game datafiles to your memory stick (location
- doesn't matter).
- - Install ScummVM like any other homebrew
- - Run ScummVM and use the launcher to add games and run them
-
+ - Copy the relevant game datafiles to your memory stick (location doesn't matter).
+ - Install ScummVM like any other homebrew.
+ - Run ScummVM and use the launcher to add games and run them.
+ - Press Start to return to the launcher and play another game.
Controls
========
-
-Left trigger - ESC
Right trigger - Modifier key (see below for uses)
+Left trigger - ESC (Usually skips cutscenes. Depends on game)
Analog - Mouse movement
-Right trigger + Analog - Fine control mouse
-Directions - Arrow keys
-Directions + Right Trigger - Diagonal arrow keys
-Triangle - Enter
-Cross - Mouse button 1
-Circle - Mouse button 2
-Square - '.' (skip dialogue in some games)
-Start - F5 (Main Menu)
-Right trigger + Start - Return-To-Launcher menu
-
-Virtual Keyboard
-================
-
-Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen.
-Start - Enter
-Right trigger - Switch to/between letter modes
-Left trigger - Switch to/between numbers and symbols
-D-Pad - Select square of characters
-Buttons/Triggers - Choose a specific character
-
+Right trigger + Analog - Fine mouse movement
+D-Pad - Arrow keys (useful mostly in SCI and AGI games)
+D-Pad + Right Trigger - Diagonal arrow keys (it's hard to input diagonals on some PSPs)
+Triangle - Enter (useful for some dialogs)
+Cross - Left Mouse Button (usually the main button)
+Circle - Right Mouse Button (secondary button in some games)
+Square - '.' (skip dialogue in some games e.g. Scumm)
+Right trigger + Square - Spacebar (useful in Gobli*ns and SCI games)
+Right trigger + Start - F5 (Main Menu in some games)
+Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen (with D-Pad).
+Right trigger + Select - Show Image Viewer (see below)
+Start - Global Menu. Allows you to 'Return To Launcher' to play another game
+
+Virtual Keyboard Mode
+=====================
+Start - Enter key. Also exits virtual keyboard mode
+Select - Exit the virtual keyboard mode
+Right trigger - Input letters: lowercase/uppercase (press to toggle)
+Left trigger - Input numbers/symbols (press to toggle)
+D-Pad - Select square of characters (up, down, left or right)
+Buttons/Triggers - Choose a specific character in the square. The four center characters are chosen
+ by the button in the corresponding position. The 2 top characters are chosen by the
+ triggers.
+Analog - Moves in a direction (left/right/up/down) (Useful to keep moving
+ while typing in AGI games among other things)
+
+
+Image Viewer
+============
+For your convenience, I've included a simple image viewer in the PSP port.
+You can view anything you want while playing a game.
+There are a few simple rules to follow:
+
+- Images must be of PNG format. If you have images in another format, many
+ graphics utilities will convert them for you.
+- Images must be named psp_image1.png, psp_image2.png etc. This is to make
+ sure there's no possible conflict between image files and game files.
+- Images must be placed in the game directories. When using the image viewer,
+ only the images of the particular game being played will be available for viewing.
+- Don't place any images in the ScummVM directory, or you won't be able to see
+ the images in the game directories.
+- There's no guarantee that you'll be able to view your image. This is because
+ big images take a lot of memory (more than the size of the image on disk). If there
+ isn't enough memory left to show the image, ScummVM will tell you so. Try to make the
+ image smaller by either shrinking it or reducing the colors to 256 color palette mode.
+
+Image Viewer Controls:
+=====================
+Left/Right - previous/next image (e.g. go from psp_image1.png to psp_image2.png)
+Up/down - zoom in/out
+Analog - move around the image
+Triggers, Start: - exit image viewer
+
+
+1st Person Game Mode (Can be ignored by most users)
+====================
+This is a special mode built for 1st person games like Lands of Lore. If you don't have these games you can
+safely ignore this mode. To enter or leave this mode, use the combo:
+
+Right Trigger + Left Trigger + Square
+
+Some buttons have been switched around to make these games more playable:
+Square - Is the modifier key instead of Right Trigger.
+Left/Right Trigger - Strafe left/right
+D-Pad Left/Right - Turn left/right
+Square + D-Pad - F1/F2/F3/F4
+Square + Select - Image Viewer
+Square + Start - Esc (shows game menu)
+
+
+
+
Notes
=====
+- Notice that you can switch between games! This is much faster than quitting
+ and having to start ScummVM all over again. Go to the global menu and choose 'Return To Launcher'.
+ (Some games may not have the Return To Launcher option available yet.)
-- Plugin files are NOT interchangeable between versions! You must copy ALL the
- plugin files that come with every version of scummvm. They sit in the /plugin
+- The PSP version of ScummVM uses the Media Engine to accelerate decoding of MP3 files. This means
+ that if you have the choice of compressing using Ogg files or MP3 files, choose MP3 -- the game
+ will generally run faster.
+
+- Plugin files are NOT interchangeable between ScummVM versions! You must copy ALL the
+ plugin files that come with every version of ScummVM. They sit in the /plugin
subdirectory. If you get a crash upon startup, try deleting all the existing
plugin files in the plugin directory and copying the new ones over again.
-- While it's possible to compress certain game resources to reduce their size,
- this can (and usually will) cause games (especially animation) to be choppy
- sometimes, as it ofcourse needs extra CPU power to decode these files.
- As such, it is recommended to play games in their original, uncompressed,
- form whenever possible.
-
- This README may be outdated, for more up-to-date instructions and notes see
the PSP Port Wiki: http://wiki.scummvm.org/index.php/PlayStation_Portable
Frequently Asked Questions
==========================
-Q: Pressing select doesn't make the virtual keyboard show up on screen!
-A: You need to make sure that the kbd.zip file is in the same directory as the scummvm executable.
-
Q: Scummvm crashes upon starting up!
A: See the first note above.
+Q: Pressing select doesn't make the virtual keyboard show up on screen!
+A: You need to make sure that the kbd.zip file is in the same directory as the ScummVM executable.
+
Q: What do I need to run the games?
-A: A 1.00 or 1.50 firmware PSP (or an EBOOT loader on firmware 2.00 or
- higher), and the necessary datafiles for the game you want to play and
- obviously this ScummVM port.
+A: A PSP that can run homebrew and the necessary datafiles for the game you want to play.
Q: Can I run game X with this?
A: You can find the list of supported games on the compatibility page
- on http://www.scummvm.org.
- Note that ScummVM is NOT a 'DOS (game) emulator', but written
- specifically for certain games/engines.
+ at http://www.scummvm.org
+ Note that ScummVM is NOT an emulator. The supported games engines have been painstakingly rewritten.
+ It's not easy to add support for a game you want that currently isn't supported.
Q: My Monkey Island 1 doesn't have any music, what gives?
A: If your version of Monkey Island came on a CD then it has the music
- as CD Audio tracks. You need to rip those to MP3/Ogg and copy them
- to the same directory as the game datafiles for music to work.
+ as CD Audio tracks. You need to rip those to MP3/Ogg (MP3 is preferred), naming them track1.mp3 track2.mp3
+ etc. and copy them to the same directory as the game datafiles for music to work.
Q: Game X crashes, or feature Y doesn't work. Can you fix this?
A: Possibly.
@@ -83,21 +132,23 @@ A: Possibly.
played them all start-to-finish on the PSP, so it's possible there
are bugs or issues that we're not aware of.
When you encounter such a bug, please use the "Bug Tracker" you find linked
- on the ScummVM website, and mention all relevant info (i.e. that you're
+ on the ScummVM website, and mention all relevant info i.e. that you're
using the PSP version, which ScummVM version it is, if the problem exists
- in a recent PC SVN version, a detailed description of the problem,
- and if at all possible a nearby savegame), this will make it much easier
+ in a recent PC version, a detailed description of the problem,
+ and if at all possible a nearby savegame. This will make it much easier
for us to reproduce (and hopefully fix) the problem.
Building the source code
========================
To build ScummVM for PSP you need:
+- ScummVM source code (svn co https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk)
+
- PSP toolchain (svn co svn://svn.pspdev.org/psp/trunk/psptoolchain)
- PSPSDK (svn co svn://svn.pspdev.org/psp/trunk/pspsdk)
- Note: This usually gets installed by the PSP toolchain,
- so you don't have to do it manually.
+ Note: This usually gets installed by the PSP toolchain,
+ so you don't have to do it manually.
- zlib (svn co svn://svn.pspdev.org/psp/trunk/zlib)
@@ -107,21 +158,15 @@ To build ScummVM for PSP you need:
- libTremor(*) (svn co svn://svn.pspdev.org/psp/trunk/libTremor)
-
-
(*) = optional
-When you've installed these libraries (read their README.PSP for instructions),
-type "make" in the backends/platform/psp directory to build a 1.00 firmware
-EBOOT.PBP, or "make kxploit" to build the 1.50/kxploit EBOOT.PBPs
-
-You can control most of the build process (engines and libraries used) from
-the Makefile.
-
+Once you've installed these libraries (read their README.PSP for instructions),
+create a subdirectory in your ScummVM folder called 'builds/psp'. Then, in this folder, type
+'../../configure --host=psp --enable-plugins --default-dynamic'. If everything is installed
+correctly, ScummVM will inform you as it finds the right components. Finally type 'make' to build.
Port Authors
============
-
Joost Peters (joostp@scummvm.org)
Paolo Costabel (paoloc@pacbell.net)
Thomas Mayer (tommybear@internode.on.net)
diff --git a/backends/platform/psp/README.PSP.in b/backends/platform/psp/README.PSP.in
index 2d53fd3b47..57058abd85 100644
--- a/backends/platform/psp/README.PSP.in
+++ b/backends/platform/psp/README.PSP.in
@@ -3,79 +3,95 @@ ScummVM-PSP @VERSION@ README
Installation
============
- - Copy the relevant game datafiles to your memory stick (location
- doesn't matter).
- - Install ScummVM like any other homebrew
- - Run ScummVM and use the launcher to add games and run them
-
+ - Copy the relevant game datafiles to your memory stick (location doesn't matter).
+ - Install ScummVM like any other homebrew.
+ - Run ScummVM and use the launcher to add games and run them.
Controls
========
-
-Left trigger - ESC
Right trigger - Modifier key (see below for uses)
+Left trigger - ESC (Usually skips cutscenes. Depends on game)
Analog - Mouse movement
-Right trigger + Analog - Fine control mouse
-Directions - Arrow keys
-Directions + Right Trigger - Diagonal arrow keys
-Triangle - Enter
-Cross - Mouse button 1
-Circle - Mouse button 2
-Square - '.' (skip dialogue in some games)
-Start - F5 (Main Menu)
-Right trigger + Start - Return-To-Launcher menu
-
-Virtual Keyboard
-================
-
-Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen.
-Start - Enter
-Right trigger - Switch to/between letter modes
-Left trigger - Switch to/between numbers and symbols
-D-Pad - Select square of characters
-Buttons/Triggers - Choose a specific character
+Right trigger + Analog - Fine mouse movement
+D-Pad - Arrow keys (useful mostly in SCI and AGI games)
+D-Pad + Right Trigger - Diagonal arrow keys (it's hard to input diagonals on some PSPs)
+Triangle - Enter (useful for some dialogs)
+Cross - Left Mouse Button (usually the main button)
+Circle - Right Mouse Button (secondary button in some games)
+Square - '.' (skip dialogue in some games e.g. Scumm)
+Right trigger + Square - Spacebar (useful in Gobli*ns and SCI games)
+Start - Global Menu. Allows you to 'Return To Launcher' to play another game
+Right trigger + Start - F5 (Main Menu in some games)
+Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen (with D-Pad).
+
+Virtual Keyboard Mode
+=====================
+Start - Enter key. Also exits virtual keyboard mode
+Select - Exit the virtual keyboard mode
+Right trigger - Input letters: lowercase/uppercase (press to toggle)
+Left trigger - Input numbers/symbols (press to toggle)
+D-Pad - Select square of characters (up, down, left or right)
+Buttons/Triggers - Choose a specific character in the square. The four center characters are chosen
+ by the button in the corresponding position. The 2 top characters are chosen by the
+ triggers.
+Analog - Moves in a direction (left/right/up/down) (Useful to keep moving
+ while typing in AGI games among other things)
+
+1st Person Game Mode (Can be ignored by most users)
+====================
+This is a special mode built for 1st person games like Lands of Lore. If you don't have these games you can
+safely ignore this mode. To enter or leave this mode, use the combo:
+
+Right Trigger + Left Trigger + Square
+
+Some buttons have been switched around to make these games more playable:
+Square - Is the modifier key instead of Right Trigger.
+Left/Right Trigger - Strafe left/right
+D-Pad Left/Right - Turn left/right
+Square + D-Pad - F1/F2/F3/F4
+Square + Start - Esc (shows game menu)
+
Notes
=====
+- Notice that you can switch between games! This is much faster than quitting
+ and having to start ScummVM all over again. Go to the global menu and choose 'Return To Launcher'.
+ (Some games may not have the Return To Launcher option available yet.)
+
+- The PSP version of ScummVM uses the Media Engine to accelerate decoding of MP3 files. This means
+ that if you have the choice of compressing using Ogg files or MP3 files, choose MP3 -- the game
+ will generally run faster.
-- Plugin files are NOT interchangeable between versions! You must copy ALL the
- plugin files that come with every version of scummvm. They sit in the /plugin
+- Plugin files are NOT interchangeable between ScummVM versions! You must copy ALL the
+ plugin files that come with every version of ScummVM. They sit in the /plugin
subdirectory. If you get a crash upon startup, try deleting all the existing
plugin files in the plugin directory and copying the new ones over again.
-- While it's possible to compress certain game resources to reduce their size,
- this can (and usually will) cause games (especially animation) to be choppy
- sometimes, as it ofcourse needs extra CPU power to decode these files.
- As such, it is recommended to play games in their original, uncompressed,
- form whenever possible.
-
- This README may be outdated, for more up-to-date instructions and notes see
the PSP Port Wiki: http://wiki.scummvm.org/index.php/PlayStation_Portable
Frequently Asked Questions
==========================
-Q: Pressing select doesn't make the virtual keyboard show up on screen!
-A: You need to make sure that the kbd.zip file is in the same directory as the scummvm executable.
-
Q: Scummvm crashes upon starting up!
A: See the first note above.
+Q: Pressing select doesn't make the virtual keyboard show up on screen!
+A: You need to make sure that the kbd.zip file is in the same directory as the ScummVM executable.
+
Q: What do I need to run the games?
-A: A 1.00 or 1.50 firmware PSP (or an EBOOT loader on firmware 2.00 or
- higher), and the necessary datafiles for the game you want to play and
- obviously this ScummVM port.
+A: A PSP that can run homebrew and the necessary datafiles for the game you want to play.
Q: Can I run game X with this?
A: You can find the list of supported games on the compatibility page
- on http://www.scummvm.org.
- Note that ScummVM is NOT a 'DOS (game) emulator', but written
- specifically for certain games/engines.
+ at http://www.scummvm.org
+ Note that ScummVM is NOT an emulator. The supported games engines have been painstakingly rewritten.
+ It's not easy to add support for a game you want that currently isn't supported.
Q: My Monkey Island 1 doesn't have any music, what gives?
A: If your version of Monkey Island came on a CD then it has the music
- as CD Audio tracks. You need to rip those to MP3/Ogg and copy them
- to the same directory as the game datafiles for music to work.
+ as CD Audio tracks. You need to rip those to MP3/Ogg (MP3 is preferred), naming them track1.mp3 track2.mp3
+ etc. and copy them to the same directory as the game datafiles for music to work.
Q: Game X crashes, or feature Y doesn't work. Can you fix this?
A: Possibly.
@@ -83,21 +99,23 @@ A: Possibly.
played them all start-to-finish on the PSP, so it's possible there
are bugs or issues that we're not aware of.
When you encounter such a bug, please use the "Bug Tracker" you find linked
- on the ScummVM website, and mention all relevant info (i.e. that you're
+ on the ScummVM website, and mention all relevant info i.e. that you're
using the PSP version, which ScummVM version it is, if the problem exists
- in a recent PC SVN version, a detailed description of the problem,
- and if at all possible a nearby savegame), this will make it much easier
+ in a recent PC version, a detailed description of the problem,
+ and if at all possible a nearby savegame. This will make it much easier
for us to reproduce (and hopefully fix) the problem.
Building the source code
========================
To build ScummVM for PSP you need:
+- ScummVM source code (svn co https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk)
+
- PSP toolchain (svn co svn://svn.pspdev.org/psp/trunk/psptoolchain)
- PSPSDK (svn co svn://svn.pspdev.org/psp/trunk/pspsdk)
- Note: This usually gets installed by the PSP toolchain,
- so you don't have to do it manually.
+ Note: This usually gets installed by the PSP toolchain,
+ so you don't have to do it manually.
- zlib (svn co svn://svn.pspdev.org/psp/trunk/zlib)
@@ -107,21 +125,15 @@ To build ScummVM for PSP you need:
- libTremor(*) (svn co svn://svn.pspdev.org/psp/trunk/libTremor)
-
-
(*) = optional
-When you've installed these libraries (read their README.PSP for instructions),
-type "make" in the backends/platform/psp directory to build a 1.00 firmware
-EBOOT.PBP, or "make kxploit" to build the 1.50/kxploit EBOOT.PBPs
-
-You can control most of the build process (engines and libraries used) from
-the Makefile.
-
+Once you've installed these libraries (read their README.PSP for instructions),
+create a subdirectory in your ScummVM folder called 'builds/psp'. Then, in this folder, type
+'../../configure --host=psp --enable-plugins --default-dynamic'. If everything is installed
+correctly, ScummVM will inform you as it finds the right components. Finally type 'make' to build.
Port Authors
============
-
Joost Peters (joostp@scummvm.org)
Paolo Costabel (paoloc@pacbell.net)
Thomas Mayer (tommybear@internode.on.net)
diff --git a/backends/platform/psp/audio.cpp b/backends/platform/psp/audio.cpp
index 14691befee..b5a72dccd0 100644
--- a/backends/platform/psp/audio.cpp
+++ b/backends/platform/psp/audio.cpp
@@ -23,10 +23,10 @@
*
*/
-#include <pspthreadman.h>
+#include <pspthreadman.h>
#include <pspaudio.h>
-
-#include "common/scummsys.h"
+
+#include "common/scummsys.h"
#include "backends/platform/psp/audio.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
@@ -39,37 +39,37 @@ bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, call
if (_init) {
PSP_ERROR("audio device already initialized\n");
return true;
- }
+ }
- PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n",
+ PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n",
freq, numOfChannels, numOfSamples, callback, (uint32)userData);
-
+
numOfSamples = PSP_AUDIO_SAMPLE_ALIGN(numOfSamples);
uint32 bufLen = numOfSamples * numOfChannels * NUM_BUFFERS * sizeof(uint16);
-
+
PSP_DEBUG_PRINT("total buffer size[%d]\n", bufLen);
-
+
_buffers[0] = (byte *)memalign(64, bufLen);
if (!_buffers[0]) {
PSP_ERROR("failed to allocate memory for audio buffers\n");
return false;
}
memset(_buffers[0], 0, bufLen); // clean the buffer
-
+
// Fill in the rest of the buffer pointers
byte *pBuffer = _buffers[0];
for (int i = 1; i < NUM_BUFFERS; i++) {
pBuffer += numOfSamples * numOfChannels * sizeof(uint16);
_buffers[i] = pBuffer;
}
-
+
// Reserve a HW channel for our audio
_pspChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, numOfSamples, numOfChannels == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO);
if (_pspChannel < 0) {
PSP_ERROR("failed to reserve audio channel\n");
return false;
}
-
+
PSP_DEBUG_PRINT("reserved channel[%d] for audio\n", _pspChannel);
// Save our data
@@ -80,17 +80,17 @@ bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, call
_userData = userData;
_bufferToFill = 0;
_bufferToPlay = 0;
-
+
_init = true;
_paused = true; // start in paused mode
-
+
threadCreateAndStart("audioThread", PRIORITY_AUDIO_THREAD, STACK_AUDIO_THREAD); // start the consumer thread
-
+
return true;
}
// The real thread function
-void PspAudio::threadFunction() {
+void PspAudio::threadFunction() {
assert(_callback);
PSP_DEBUG_PRINT_FUNC("audio thread started\n");
@@ -108,12 +108,12 @@ void PspAudio::threadFunction() {
PSP_DEBUG_PRINT("filling buffer[%d]\n", _bufferToFill);
_callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in data
nextBuffer(_bufferToFill);
-
+
PSP_DEBUG_PRINT("playing buffer[%d].\n", _bufferToPlay);
playBuffer();
nextBuffer(_bufferToPlay);
} // while _init
-
+
// destroy everything
free(_buffers[0]);
sceAudioChRelease(_pspChannel);
@@ -136,7 +136,7 @@ inline bool PspAudio::playBuffer() {
ret = sceAudioOutputBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
else
ret = sceAudioOutputPannedBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
-
+
if (ret < 0) {
PSP_ERROR("failed to output audio. Error[%d]\n", ret);
return false;
@@ -146,5 +146,5 @@ inline bool PspAudio::playBuffer() {
void PspAudio::close() {
PSP_DEBUG_PRINT("close has been called ***************\n");
- _init = false;
+ _init = false;
}
diff --git a/backends/platform/psp/audio.h b/backends/platform/psp/audio.h
index 07f70cec7d..f5179028dc 100644
--- a/backends/platform/psp/audio.h
+++ b/backends/platform/psp/audio.h
@@ -35,9 +35,9 @@ public:
FREQUENCY = 44100 /* only frequency we allow */
};
typedef void (* callbackFunc)(void *userData, byte *samples, int len); // audio callback to call
- PspAudio() : _pspChannel(0),
- _numOfChannels(0), _numOfSamples(0), _callback(0),
- _bufferToPlay(0), _bufferToFill(0),
+ PspAudio() : _pspChannel(0),
+ _numOfChannels(0), _numOfSamples(0), _callback(0),
+ _bufferToPlay(0), _bufferToFill(0),
_init(false), _paused(true) {
for (int i=0; i<NUM_BUFFERS; i++)
_buffers[i] = 0;
@@ -51,7 +51,7 @@ public:
void pause() { _paused = true; }
void unpause() { _paused = false; }
virtual void threadFunction(); // actual audio thread
-
+
private:
int _pspChannel; // chosen hardware output channel
uint32 _numOfChannels; // 1 for mono; 2 for stereo
diff --git a/backends/platform/psp/cursor.h b/backends/platform/psp/cursor.h
index c3d4d76803..2ff0415c6c 100644
--- a/backends/platform/psp/cursor.h
+++ b/backends/platform/psp/cursor.h
@@ -26,6 +26,8 @@
#ifndef MOUSE_H
#define MOUSE_H
+#include "backends/platform/psp/default_display_client.h"
+
class Cursor : public DefaultDisplayClient {
private:
int _hotspotX, _hotspotY;
diff --git a/backends/platform/psp/display_client.cpp b/backends/platform/psp/display_client.cpp
index 6360772c96..4310b195dd 100644
--- a/backends/platform/psp/display_client.cpp
+++ b/backends/platform/psp/display_client.cpp
@@ -32,6 +32,7 @@
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/display_manager.h"
+#define PSP_INCLUDE_SWAP
#include "backends/platform/psp/memory.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
@@ -341,14 +342,13 @@ void Buffer::copyFromRect(const byte *buf, uint32 pitch, int destX, int destY, u
if (pitch == realWidthInBytes && pitch == recWidthInBytes) {
//memcpy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
if (_pixelFormat.swapRB)
- PspMemory::fastSwap(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth), _pixelFormat);
+ PspMemorySwap::fastSwap(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth), _pixelFormat);
else
PspMemory::fastCopy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
} else {
do {
- //memcpy(dst, buf, recWidthInBytes);
if (_pixelFormat.swapRB)
- PspMemory::fastSwap(dst, buf, recWidthInBytes, _pixelFormat);
+ PspMemorySwap::fastSwap(dst, buf, recWidthInBytes, _pixelFormat);
else
PspMemory::fastCopy(dst, buf, recWidthInBytes);
buf += pitch;
@@ -370,7 +370,7 @@ void Buffer::copyToArray(byte *dst, int pitch) {
do {
//memcpy(dst, src, sourceWidthInBytes);
if (_pixelFormat.swapRB)
- PspMemory::fastSwap(dst, src, sourceWidthInBytes, _pixelFormat);
+ PspMemorySwap::fastSwap(dst, src, sourceWidthInBytes, _pixelFormat);
else
PspMemory::fastCopy(dst, src, sourceWidthInBytes);
src += realWidthInBytes;
@@ -378,45 +378,45 @@ void Buffer::copyToArray(byte *dst, int pitch) {
} while (--h);
}
-/* We can size the buffer either by texture size (multiple of 2^n) or source size. The GU can
- really handle both, but is supposed to get only 2^n size buffers */
void Buffer::setSize(uint32 width, uint32 height, HowToSize textureOrSource/*=kSizeByTextureSize*/) {
DEBUG_ENTER_FUNC();
- PSP_DEBUG_PRINT("w[%u], h[%u], %s\n", width, height, textureOrSource ? "size by source" : "size by texture");
-
+
+ // We can size the buffer either by texture size (multiple of 2^n) or source size.
+ // At higher sizes, increasing the texture size to 2^n is a waste of space. At these sizes kSizeBySourceSize should be used.
+
_sourceSize.width = width;
_sourceSize.height = height;
- _textureSize.width = scaleUpToPowerOfTwo(width);
+ _textureSize.width = scaleUpToPowerOfTwo(width); // can only scale up to 512
_textureSize.height = scaleUpToPowerOfTwo(height);
-
+
if (textureOrSource == kSizeByTextureSize) {
_width = _textureSize.width;
_height = _textureSize.height;
- } else { /* kSizeBySourceSize */
- _width = _sourceSize.width;
+ } else { // sizeBySourceSize
+ _width = _sourceSize.width;
_height = _sourceSize.height;
+
+ // adjust allocated width to be divisible by 32.
+ // The GU can only handle multiples of 16 bytes. A 4 bit image x 32 will give us 16 bytes
+ // We don't necessarily know the depth of the pixels here. So just make it divisible by 32.
+ uint32 checkDiv = _width & 31;
+ if (checkDiv)
+ _width += 32 - checkDiv;
}
+
+ PSP_DEBUG_PRINT("width[%u], height[%u], texW[%u], texH[%u], sourceW[%d], sourceH[%d] %s\n", _width, _height, _textureSize.width, _textureSize.height, _sourceSize.width, _sourceSize.height, textureOrSource ? "size by source" : "size by texture");
}
-/* Scale a dimension (width/height) up to power of 2 for the texture */
+// Scale a dimension (width/height) up to power of 2 for the texture
+// Will only go up to 512 since that's the maximum PSP texture size
uint32 Buffer::scaleUpToPowerOfTwo(uint32 size) {
- uint32 textureDimension = 0;
- if (size <= 16)
- textureDimension = 16;
- else if (size <= 32)
- textureDimension = 32;
- else if (size <= 64)
- textureDimension = 64;
- else if (size <= 128)
- textureDimension = 128;
- else if (size <= 256)
- textureDimension = 256;
- else
- textureDimension = 512;
+ uint32 textureDimension = 16;
+ while (size > textureDimension && textureDimension < 512)
+ textureDimension <<= 1;
- PSP_DEBUG_PRINT("power of 2 = %u\n", textureDimension);
+ PSP_DEBUG_PRINT("size[%u]. power of 2[%u]\n", size, textureDimension);
return textureDimension;
}
@@ -539,51 +539,41 @@ void GuRenderer::render() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("Buffer[%p] Palette[%p]\n", _buffer->getPixels(), _palette->getRawValues());
- setMaxTextureOffsetByIndex(0, 0);
-
guProgramDrawBehavior();
if (_buffer->hasPalette())
guLoadPalette();
guProgramTextureFormat();
- guLoadTexture();
-
- Vertex *vertices = guGetVertices();
- fillVertices(vertices);
- guDrawVertices(vertices);
-
- if (_buffer->getSourceWidth() > 512) {
- setMaxTextureOffsetByIndex(1, 0);
-
- guLoadTexture();
-
- vertices = guGetVertices();
- fillVertices(vertices);
-
- guDrawVertices(vertices);
+ // Loop over patches of 512x512 pixel textures and draw them
+ for (uint32 j = 0; j < _buffer->getSourceHeight(); j += 512) {
+ _textureLoadOffset.y = j;
+
+ for (uint32 i = 0; i < _buffer->getSourceWidth(); i += 512) {
+ _textureLoadOffset.x = i;
+
+ guLoadTexture();
+ Vertex *vertices = guGetVertices();
+ fillVertices(vertices);
+
+ guDrawVertices(vertices);
+ }
}
}
-inline void GuRenderer::setMaxTextureOffsetByIndex(uint32 x, uint32 y) {
- DEBUG_ENTER_FUNC();
- const uint32 maxTextureSizeShift = 9; /* corresponds to 512 = max texture size*/
-
- _maxTextureOffset.x = x << maxTextureSizeShift; /* x times 512 */
- _maxTextureOffset.y = y << maxTextureSizeShift; /* y times 512 */
-}
-
inline void GuRenderer::guProgramDrawBehavior() {
DEBUG_ENTER_FUNC();
- PSP_DEBUG_PRINT("blending[%s] colorTest[%s] reverseAlpha[%s] keyColor[%u]\n", _blending ? "on" : "off", _colorTest ? "on" : "off", _alphaReverse ? "on" : "off", _keyColor);
+ PSP_DEBUG_PRINT("blending[%s] colorTest[%s] reverseAlpha[%s] keyColor[%u]\n",
+ _blending ? "on" : "off", _colorTest ? "on" : "off",
+ _alphaReverse ? "on" : "off", _keyColor);
if (_blending) {
sceGuEnable(GU_BLEND);
- if (_alphaReverse) // Reverse the alpha value (0 is 1)
+ if (_alphaReverse) // Reverse the alpha value (ie. 0 is 1) easier to do in some cases
sceGuBlendFunc(GU_ADD, GU_ONE_MINUS_SRC_ALPHA, GU_SRC_ALPHA, 0, 0);
- else // Normal alpha values
+ else // Normal alpha values
sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
} else
@@ -591,7 +581,9 @@ inline void GuRenderer::guProgramDrawBehavior() {
if (_colorTest) {
sceGuEnable(GU_COLOR_TEST);
- sceGuColorFunc(GU_NOTEQUAL, _keyColor, 0x00ffffff);
+ sceGuColorFunc(GU_NOTEQUAL, // show only colors not equal to this color
+ _keyColor,
+ 0x00ffffff); // match everything but alpha
} else
sceGuDisable(GU_COLOR_TEST);
}
@@ -612,7 +604,8 @@ inline void GuRenderer::guLoadPalette() {
PSP_DEBUG_PRINT("bpp[%d], pixelformat[%d], mask[%x]\n", _buffer->getBitsPerPixel(), _palette->getPixelFormat(), mask);
sceGuClutMode(convertToGuPixelFormat(_palette->getPixelFormat()), 0, mask, 0);
- sceGuClutLoad(_palette->getNumOfEntries() >> 3, _palette->getRawValues());
+ sceGuClutLoad(_palette->getNumOfEntries() >> 3, // it's in batches of 8 for some reason
+ _palette->getRawValues());
}
inline void GuRenderer::guProgramTextureFormat() {
@@ -658,7 +651,17 @@ inline uint32 GuRenderer::convertToGuPixelFormat(PSPPixelFormat::Type format) {
inline void GuRenderer::guLoadTexture() {
DEBUG_ENTER_FUNC();
- sceGuTexImage(0, _buffer->getTextureWidth(), _buffer->getTextureHeight(), _buffer->getWidth(), _buffer->getPixels() + _buffer->_pixelFormat.pixelsToBytes(_maxTextureOffset.x));
+ byte *startPoint = _buffer->getPixels();
+ if (_textureLoadOffset.x)
+ startPoint += _buffer->_pixelFormat.pixelsToBytes(_textureLoadOffset.x);
+ if (_textureLoadOffset.y)
+ startPoint += _buffer->getWidthInBytes() * _textureLoadOffset.y;
+
+ sceGuTexImage(0,
+ _buffer->getTextureWidth(), // texture width (must be power of 2)
+ _buffer->getTextureHeight(), // texture height (must be power of 2)
+ _buffer->getWidth(), // width of a line of the image (to get to the next line)
+ startPoint); // where to start reading
}
inline Vertex *GuRenderer::guGetVertices() {
@@ -676,40 +679,40 @@ void GuRenderer::fillVertices(Vertex *vertices) {
uint32 outputWidth = _displayManager->getOutputWidth();
uint32 outputHeight = _displayManager->getOutputHeight();
- float textureStartX, textureStartY, textureEndX, textureEndY;
-
// Texture adjustments for eliminating half-pixel artifacts from scaling
// Not necessary if we don't scale
- float textureAdjustment = 0.0f;
+ float textureFix = 0.0f;
if (_useGlobalScaler &&
- (_displayManager->getScaleX() != 1.0f || _displayManager->getScaleX() != 1.0f))
- textureAdjustment = 0.5f;
-
- textureStartX = textureAdjustment + _offsetInBuffer.x; //debug
- textureStartY = textureAdjustment + _offsetInBuffer.y;
- // We subtract maxTextureOffset because our shifted texture starts at 512 and will go to 640
- textureEndX = _offsetInBuffer.x + _drawSize.width - textureAdjustment - _maxTextureOffset.x;
- textureEndY = _offsetInBuffer.y + _drawSize.height - textureAdjustment;
-
+ (_displayManager->getScaleX() != 1.0f || _displayManager->getScaleY() != 1.0f))
+ textureFix = 0.5f;
+
+ // These coordinates describe an area within the texture. ie. we already loaded a square of texture,
+ // now the coordinates within it are 0 to the edge of the area of the texture we want to draw
+ float textureStartX = textureFix + _offsetInBuffer.x;
+ float textureStartY = textureFix + _offsetInBuffer.y;
+ // even when we draw one of several textures, we use the whole drawsize of the image. The GU
+ // will draw what it can with the texture it has and scale it properly for us.
+ float textureEndX = -textureFix + _offsetInBuffer.x + _drawSize.width - _textureLoadOffset.x;
+ float textureEndY = -textureFix + _offsetInBuffer.y + _drawSize.height - _textureLoadOffset.y;
// For scaling to the final image size, calculate the gaps on both sides
uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0;
uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0;
// Save scaled offset on screen
- float scaledOffsetOnScreenX = scaleSourceToOutputX(_offsetOnScreen.x);
- float scaledOffsetOnScreenY = scaleSourceToOutputY(_offsetOnScreen.y);
-
- float imageStartX, imageStartY, imageEndX, imageEndY;
+ float scaledOffsetOnScreenX = scaleSourceToOutput(true, _offsetOnScreen.x);
+ float scaledOffsetOnScreenY = scaleSourceToOutput(false, _offsetOnScreen.y);
- imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutputX(_maxTextureOffset.x));
- imageStartY = gapY + scaledOffsetOnScreenY;
+ float imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutput(true, stretch(true, _textureLoadOffset.x)));
+ float imageStartY = gapY + scaledOffsetOnScreenY + (scaleSourceToOutput(false, stretch(false, _textureLoadOffset.y)));
+ float imageEndX, imageEndY;
+
if (_fullScreen) { // shortcut
- imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX;
+ imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX;
imageEndY = PSP_SCREEN_HEIGHT - gapY + scaledOffsetOnScreenY; // needed for screen shake
} else { /* !fullScreen */
- imageEndX = imageStartX + scaleSourceToOutputX(_drawSize.width);
- imageEndY = imageStartY + scaleSourceToOutputY(_drawSize.height);
+ imageEndX = gapX + scaledOffsetOnScreenX + scaleSourceToOutput(true, stretch(true, _drawSize.width));
+ imageEndY = gapY + scaledOffsetOnScreenY + scaleSourceToOutput(false, stretch(false, _drawSize.height));
}
vertices[0].u = textureStartX;
@@ -728,8 +731,8 @@ void GuRenderer::fillVertices(Vertex *vertices) {
PSP_DEBUG_PRINT("ImageStart: X[%f] Y[%f] ImageEnd: X[%.1f] Y[%.1f]\n", imageStartX, imageStartY, imageEndX, imageEndY);
}
-/* Scale the input X offset to appear in proper position on the screen */
-inline float GuRenderer::scaleSourceToOutputX(float offset) {
+/* Scale the input X/Y offset to appear in proper position on the screen */
+inline float GuRenderer::scaleSourceToOutput(bool x, float offset) {
float result;
if (!_useGlobalScaler)
@@ -737,28 +740,22 @@ inline float GuRenderer::scaleSourceToOutputX(float offset) {
else if (!offset)
result = 0.0f;
else
- result = offset * _displayManager->getScaleX();
+ result = x ? offset * _displayManager->getScaleX() : offset * _displayManager->getScaleY();
return result;
}
-/* Scale the input Y offset to appear in proper position on the screen */
-inline float GuRenderer::scaleSourceToOutputY(float offset) {
- float result;
-
- if (!_useGlobalScaler)
- result = offset;
- else if (!offset)
- result = 0.0f;
- else
- result = offset * _displayManager->getScaleY();
-
- return result;
+/* Scale the input X/Y offset to appear in proper position on the screen */
+inline float GuRenderer::stretch(bool x, float size) {
+ if (!_stretch)
+ return size;
+ return (x ? size * _stretchX : size * _stretchY);
}
inline void GuRenderer::guDrawVertices(Vertex *vertices) {
DEBUG_ENTER_FUNC();
+ // This function shouldn't need changing. The '32' here refers to floating point vertices.
sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, vertices);
}
diff --git a/backends/platform/psp/display_client.h b/backends/platform/psp/display_client.h
index d05b0b046c..005fc76c7c 100644
--- a/backends/platform/psp/display_client.h
+++ b/backends/platform/psp/display_client.h
@@ -30,6 +30,7 @@
#include "graphics/surface.h"
#include "common/system.h"
#include "backends/platform/psp/memory.h"
+#include "backends/platform/psp/psppixelformat.h"
#define MAX_TEXTURE_SIZE 512
@@ -173,8 +174,13 @@ protected:
class GuRenderer {
public:
// Constructors
- GuRenderer() : _useGlobalScaler(false), _buffer(0), _palette(0), _blending(false), _alphaReverse(false), _colorTest(false), _keyColor(0), _fullScreen(false) {}
- GuRenderer(Buffer *buffer, Palette *palette) : _useGlobalScaler(false), _buffer(buffer), _palette(palette), _blending(false), _alphaReverse(false), _colorTest(false), _keyColor(0), _fullScreen(false) {}
+ GuRenderer() : _useGlobalScaler(false), _buffer(0), _palette(0),
+ _blending(false), _alphaReverse(false), _colorTest(false),
+ _keyColor(0), _fullScreen(false), _stretch(false), _stretchX(1.0f), _stretchY(1.0f) {}
+ GuRenderer(Buffer *buffer, Palette *palette) :
+ _useGlobalScaler(false), _buffer(buffer), _palette(palette),
+ _blending(false), _alphaReverse(false), _colorTest(false),
+ _keyColor(0), _fullScreen(false), _stretch(false), _stretchX(1.0f), _stretchY(1.0f) {}
static void setDisplayManager(DisplayManager *dm) { _displayManager = dm; } // Called by the Display Manager
// Setters
@@ -189,8 +195,7 @@ public:
}
void setBuffer(Buffer *buffer) { _buffer = buffer; }
void setPalette(Palette *palette) { _palette = palette; }
- void setMaxTextureOffsetByIndex(uint32 x, uint32 y); // For drawing multiple textures
- void setOffsetOnScreen(uint32 x, uint32 y) { _offsetOnScreen.x = x; _offsetOnScreen.y = y; }
+ void setOffsetOnScreen(int x, int y) { _offsetOnScreen.x = x; _offsetOnScreen.y = y; }
void setOffsetInBuffer(uint32 x, uint32 y) { _offsetInBuffer.x = x; _offsetInBuffer.y = y; }
void setColorTest(bool value) { _colorTest = value; }
void setKeyColor(uint32 value) { _keyColor = _buffer->_pixelFormat.convertTo32BitColor(value); }
@@ -198,6 +203,8 @@ public:
void setAlphaReverse(bool value) { _alphaReverse = value; }
void setFullScreen(bool value) { _fullScreen = value; } // Shortcut for rendering
void setUseGlobalScaler(bool value) { _useGlobalScaler = value; } // Scale to screen
+ void setStretch(bool active) { _stretch = active; }
+ void setStretchXY(float x, float y) { _stretchX = x; _stretchY = y; }
static void cacheInvalidate(void *pointer, uint32 size);
@@ -215,11 +222,11 @@ protected:
void guDrawVertices(Vertex *vertices);
uint32 convertToGuPixelFormat(PSPPixelFormat::Type format);
- float scaleSourceToOutputX(float offset);
- float scaleSourceToOutputY(float offset);
+ float scaleSourceToOutput(bool x, float offset);
+ float stretch(bool x, float size);
friend class MasterGuRenderer;
- Point _maxTextureOffset; ///> For rendering textures > 512 pixels
+ Point _textureLoadOffset; ///> For rendering textures > 512 pixels
Point _offsetOnScreen; ///> Where on screen to draw
Point _offsetInBuffer; ///> Where in the texture to draw
bool _useGlobalScaler; ///> Scale to the output size on screen
@@ -232,6 +239,8 @@ protected:
bool _colorTest;
uint32 _keyColor; ///> Color to test against for color test. in 32 bits.
bool _fullScreen; ///> Speeds up for fullscreen rendering
+ bool _stretch; ///> Whether zooming is activated
+ float _stretchX, _stretchY;
};
#endif /* PSP_SCREEN_H */
diff --git a/backends/platform/psp/display_manager.cpp b/backends/platform/psp/display_manager.cpp
index e65d6f4fd0..7454211da6 100644
--- a/backends/platform/psp/display_manager.cpp
+++ b/backends/platform/psp/display_manager.cpp
@@ -34,6 +34,7 @@
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"
#include "backends/platform/psp/pspkeyboard.h"
+#include "backends/platform/psp/image_viewer.h"
#define USE_DISPLAY_CALLBACK // to use callback for finishing the render
#include "backends/platform/psp/display_manager.h"
@@ -59,28 +60,93 @@ const OSystem::GraphicsMode DisplayManager::_supportedModes[] = {
{0, 0, 0}
};
+
+// Class VramAllocator -----------------------------------
+
+DECLARE_SINGLETON(VramAllocator)
+
+//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
+//#define __PSP_DEBUG_PRINT__
+
+#include "backends/platform/psp/trace.h"
+
+
+void *VramAllocator::allocate(int32 size, bool smallAllocation /* = false */) {
+ DEBUG_ENTER_FUNC();
+ assert(size > 0);
+
+ byte *lastAddress = smallAllocation ? (byte *)VRAM_SMALL_ADDRESS : (byte *)VRAM_START_ADDRESS;
+ Common::List<Allocation>::iterator i;
+
+ // Find a block that fits, starting from the beginning
+ for (i = _allocList.begin(); i != _allocList.end(); ++i) {
+ byte *currAddress = (*i).address;
+
+ if (currAddress - lastAddress >= size) // We found a match
+ break;
+
+ if ((*i).getEnd() > lastAddress)
+ lastAddress = (byte *)(*i).getEnd();
+ }
+
+ if (lastAddress + size > (byte *)VRAM_END_ADDRESS) {
+ PSP_DEBUG_PRINT("No space for allocation of %d bytes. %d bytes already allocated.\n",
+ size, _bytesAllocated);
+ return NULL;
+ }
+
+ _allocList.insert(i, Allocation(lastAddress, size));
+ _bytesAllocated += size;
+
+ PSP_DEBUG_PRINT("Allocated in VRAM, size %u at %p.\n", size, lastAddress);
+ PSP_DEBUG_PRINT("Total allocated %u, remaining %u.\n", _bytesAllocated, (2 * 1024 * 1024) - _bytesAllocated);
+
+ return lastAddress;
+}
+
+// Deallocate a block from VRAM
+void VramAllocator::deallocate(void *address) {
+ DEBUG_ENTER_FUNC();
+ address = (byte *)CACHED(address); // Make sure all addresses are the same
+
+ Common::List<Allocation>::iterator i;
+
+ // Find the Allocator to deallocate
+ for (i = _allocList.begin(); i != _allocList.end(); ++i) {
+ if ((*i).address == address) {
+ _bytesAllocated -= (*i).size;
+ _allocList.erase(i);
+ PSP_DEBUG_PRINT("Deallocated address[%p], size[%u]\n", (*i).address, (*i).size);
+ return;
+ }
+ }
+
+ PSP_DEBUG_PRINT("Address[%p] not allocated.\n", address);
+}
+
+
// Class MasterGuRenderer ----------------------------------------------
void MasterGuRenderer::setupCallbackThread() {
DEBUG_ENTER_FUNC();
-
+
// start the thread that updates the display
- threadCreateAndStart("DisplayCbThread", PRIORITY_DISPLAY_THREAD, STACK_DISPLAY_THREAD);
+ threadCreateAndStart("DisplayCbThread", PRIORITY_DISPLAY_THREAD, STACK_DISPLAY_THREAD);
}
// this function gets called by PspThread when starting the new thread
void MasterGuRenderer::threadFunction() {
DEBUG_ENTER_FUNC();
-
+
// Create the callback. It should always get the pointer to MasterGuRenderer
_callbackId = sceKernelCreateCallback("Display Callback", guCallback, this);
if (_callbackId < 0) {
- PSP_ERROR("failed to create display callback\n");
+ PSP_ERROR("failed to create display callback\n");
}
-
+
PSP_DEBUG_PRINT("created callback. Going to sleep\n");
- sceKernelSleepThreadCB(); // sleep until we get a callback
+ sceKernelSleepThreadCB(); // sleep until we get a callback
}
// This callback is called when the render is finished. It swaps the buffers
@@ -88,12 +154,12 @@ int MasterGuRenderer::guCallback(int, int, void *__this) {
MasterGuRenderer *_this = (MasterGuRenderer *)__this;
- sceGuSync(0, 0); // make sure we wait for GU to finish
+ sceGuSync(0, 0); // make sure we wait for GU to finish
sceDisplayWaitVblankStartCB(); // wait for v-blank without eating main thread cycles
sceGuSwapBuffers(); // swap the back and front buffers
_this->_renderFinished = true; // Only this thread can set the variable to true
-
+
return 0;
}
@@ -149,7 +215,7 @@ inline void MasterGuRenderer::guPreRender() {
DEBUG_ENTER_FUNC();
_renderFinished = false; // set to synchronize with callback thread
-
+
#ifdef ENABLE_RENDER_MEASURE
_lastRenderTime = g_system->getMillis();
#endif /* ENABLE_RENDER_MEASURE */
@@ -178,7 +244,7 @@ inline void MasterGuRenderer::guPostRender() {
else
sceKernelNotifyCallback(_callbackId, 0); // notify the callback. Nothing extra to pass
#else
- sceGuSync(0, 0);
+ sceGuSync(0, 0);
#ifdef ENABLE_RENDER_MEASURE
uint32 now = g_system->getMillis();
@@ -188,7 +254,7 @@ inline void MasterGuRenderer::guPostRender() {
sceDisplayWaitVblankStart();
sceGuSwapBuffers();
_renderFinished = true;
-#endif /* !USE_DISPLAY_CALLBACK */
+#endif /* !USE_DISPLAY_CALLBACK */
}
void MasterGuRenderer::guShutDown() {
@@ -216,7 +282,7 @@ void DisplayManager::init() {
#ifdef USE_DISPLAY_CALLBACK
_masterGuRenderer.setupCallbackThread();
#endif
-
+
}
void DisplayManager::setSizeAndPixelFormat(uint width, uint height, const Graphics::PixelFormat *format) {
@@ -286,10 +352,10 @@ void DisplayManager::calculateScaleParams() {
break;
case KEEP_ASPECT_RATIO: { // maximize the height while keeping aspect ratio
float aspectRatio = (float)_displayParams.screenSource.width / (float)_displayParams.screenSource.height;
-
+
_displayParams.screenOutput.height = PSP_SCREEN_HEIGHT; // always full height
_displayParams.screenOutput.width = (uint32)(PSP_SCREEN_HEIGHT * aspectRatio);
-
+
if (_displayParams.screenOutput.width > PSP_SCREEN_WIDTH) // we can't have wider than the screen
_displayParams.screenOutput.width = PSP_SCREEN_WIDTH;
}
@@ -301,7 +367,7 @@ void DisplayManager::calculateScaleParams() {
default:
PSP_ERROR("Unsupported graphics mode[%d].\n", _graphicsMode);
}
-
+
// calculate scale factors for X and Y
_displayParams.scaleX = ((float)_displayParams.screenOutput.width) / _displayParams.screenSource.width;
_displayParams.scaleY = ((float)_displayParams.screenOutput.height) / _displayParams.screenSource.height;
@@ -316,51 +382,54 @@ bool DisplayManager::renderAll() {
if (!_masterGuRenderer.isRenderFinished()) {
PSP_DEBUG_PRINT("Callback render not finished.\n");
return false; // didn't render
- }
+ }
#endif /* USE_DISPLAY_CALLBACK */
-
+
// This is cheaper than checking time, so we do it first
+ // Any one of these being dirty causes everything to draw
if (!_screen->isDirty() &&
- (!_overlay->isDirty()) &&
- (!_cursor->isDirty()) &&
- (!_keyboard->isDirty())) {
+ !_overlay->isDirty() &&
+ !_cursor->isDirty() &&
+ !_keyboard->isDirty() &&
+ !_imageViewer->isDirty()) {
PSP_DEBUG_PRINT("Nothing dirty\n");
return true; // nothing to render
}
- if (!isTimeToUpdate())
+ if (!isTimeToUpdate())
return false; // didn't render
- PSP_DEBUG_PRINT("screen[%s], overlay[%s], cursor[%s], keyboard[%s]\n",
+ PSP_DEBUG_PRINT("dirty: screen[%s], overlay[%s], cursor[%s], keyboard[%s], imageViewer[%s]\n",
_screen->isDirty() ? "true" : "false",
_overlay->isDirty() ? "true" : "false",
_cursor->isDirty() ? "true" : "false",
- _keyboard->isDirty() ? "true" : "false"
+ _keyboard->isDirty() ? "true" : "false",
+ _imageViewer->isDirty() ? "true" : "false",
);
_masterGuRenderer.guPreRender(); // Set up rendering
_screen->render();
-
_screen->setClean(); // clean out dirty bit
+
+ if (_imageViewer->isVisible())
+ _imageViewer->render();
+ _imageViewer->setClean();
if (_overlay->isVisible())
- _overlay->render();
-
+ _overlay->render();
_overlay->setClean();
if (_cursor->isVisible())
_cursor->render();
-
_cursor->setClean();
if (_keyboard->isVisible())
_keyboard->render();
-
_keyboard->setClean();
-
- _masterGuRenderer.guPostRender();
+ _masterGuRenderer.guPostRender();
+
return true; // rendered successfully
}
diff --git a/backends/platform/psp/display_manager.h b/backends/platform/psp/display_manager.h
index 626415696a..5c7d4c799a 100644
--- a/backends/platform/psp/display_manager.h
+++ b/backends/platform/psp/display_manager.h
@@ -27,6 +27,48 @@
#define PSP_DISPLAY_MAN_H
#include "backends/platform/psp/thread.h"
+#include "common/list.h"
+
+#define UNCACHED(x) ((byte *)(((uint32)(x)) | 0x40000000)) /* make an uncached access */
+#define CACHED(x) ((byte *)(((uint32)(x)) & 0xBFFFFFFF)) /* make an uncached access into a cached one */
+
+/**
+ * Class that allocates memory in the VRAM
+ */
+class VramAllocator : public Common::Singleton<VramAllocator> {
+public:
+ VramAllocator() : _bytesAllocated(0) {}
+ void *allocate(int32 size, bool smallAllocation = false); // smallAllocation e.g. palettes
+ void deallocate(void *pointer);
+
+ static inline bool isAddressInVram(void *address) {
+ if ((uint32)(CACHED(address)) >= VRAM_START_ADDRESS && (uint32)(CACHED(address)) < VRAM_END_ADDRESS)
+ return true;
+ return false;
+ }
+
+
+private:
+ /**
+ * Used to allocate in VRAM
+ */
+ struct Allocation {
+ byte *address;
+ uint32 size;
+ void *getEnd() { return address + size; }
+ Allocation(void *Address, uint32 Size) : address((byte *)Address), size(Size) {}
+ Allocation() : address(0), size(0) {}
+ };
+
+ enum {
+ VRAM_START_ADDRESS = 0x04000000,
+ VRAM_END_ADDRESS = 0x04200000,
+ VRAM_SMALL_ADDRESS = VRAM_END_ADDRESS - (4 * 1024) // 4K in the end for small allocations
+ };
+ Common::List <Allocation> _allocList; // List of allocations
+ uint32 _bytesAllocated;
+};
+
/**
* Class used only by DisplayManager to start/stop GU rendering
@@ -39,7 +81,7 @@ public:
void guPostRender();
void guShutDown();
bool isRenderFinished() { return _renderFinished; }
- void setupCallbackThread();
+ void setupCallbackThread();
private:
virtual void threadFunction(); // for the display callback thread
static uint32 _displayList[];
@@ -47,13 +89,14 @@ private:
void guProgramDisplayBufferSizes();
static int guCallback(int, int, void *__this); // for the display callback
bool _renderFinished; // for sync with render callback
- int _callbackId; // to keep track of render callback
+ int _callbackId; // to keep track of render callback
};
class Screen;
class Overlay;
class Cursor;
class PSPKeyboard;
+class ImageViewer;
/**
* Class that manages all display clients
@@ -65,7 +108,8 @@ public:
KEEP_ASPECT_RATIO,
STRETCHED_FULL_SCREEN
};
- DisplayManager() : _screen(0), _cursor(0), _overlay(0), _keyboard(0), _lastUpdateTime(0), _graphicsMode(0) {}
+ DisplayManager() : _screen(0), _cursor(0), _overlay(0), _keyboard(0),
+ _imageViewer(0), _lastUpdateTime(0), _graphicsMode(0) {}
~DisplayManager();
void init();
@@ -76,11 +120,13 @@ public:
uint32 getDefaultGraphicsMode() const { return STRETCHED_FULL_SCREEN; }
const OSystem::GraphicsMode* getSupportedGraphicsModes() const { return _supportedModes; }
- // Setters
+ // Setters for pointers
void setScreen(Screen *screen) { _screen = screen; }
void setCursor(Cursor *cursor) { _cursor = cursor; }
void setOverlay(Overlay *overlay) { _overlay = overlay; }
void setKeyboard(PSPKeyboard *keyboard) { _keyboard = keyboard; }
+ void setImageViewer(ImageViewer *imageViewer) { _imageViewer = imageViewer; }
+
void setSizeAndPixelFormat(uint width, uint height, const Graphics::PixelFormat *format);
// Getters
@@ -106,6 +152,7 @@ private:
Cursor *_cursor;
Overlay *_overlay;
PSPKeyboard *_keyboard;
+ ImageViewer *_imageViewer;
MasterGuRenderer _masterGuRenderer;
uint32 _lastUpdateTime; // For limiting FPS
diff --git a/backends/platform/psp/dummy.cpp b/backends/platform/psp/dummy.cpp
new file mode 100644
index 0000000000..4236734d4b
--- /dev/null
+++ b/backends/platform/psp/dummy.cpp
@@ -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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $
+ * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $
+ *
+ */
+
+ //#include "common/scummsys.h"
+ #include <time.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <png.h>
+ #include <sys/socket.h>
+
+//void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) {
+//}
+
+//void userFlushFn(png_structp png_ptr) {
+//}
+
+ // Dummy functions are pulled in so that we don't need to build the plugins with certain libs
+
+ int dummyFunc() {
+
+ // For Broken Sword 2.5
+ volatile int i;
+ i = clock();
+ rename("dummyA", "dummyB");
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_set_write_fn(png_ptr, NULL, NULL, NULL);
+ png_infop info_ptr;
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ // For lua's usage of libc: very heavy usage so it pulls in sockets?
+ setsockopt(0, 0, 0, NULL, 0);
+ getsockopt(0, 0, 0, NULL, NULL);
+
+ return i;
+} \ No newline at end of file
diff --git a/backends/platform/psp/image_viewer.cpp b/backends/platform/psp/image_viewer.cpp
new file mode 100644
index 0000000000..26b7f31c97
--- /dev/null
+++ b/backends/platform/psp/image_viewer.cpp
@@ -0,0 +1,327 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $
+ * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/archive.h"
+#include "common/events.h"
+#include "common/ptr.h"
+#include "gui/message.h"
+#include "engines/engine.h"
+#include "backends/platform/psp/input.h"
+#include "backends/platform/psp/display_manager.h"
+#include "backends/platform/psp/display_client.h"
+#include "backends/platform/psp/image_viewer.h"
+#include "backends/platform/psp/png_loader.h"
+#include "backends/platform/psp/thread.h"
+
+static const char *imageName = "psp_image";
+#define PSP_SCREEN_HEIGHT 272
+#define PSP_SCREEN_WIDTH 480
+
+bool ImageViewer::load(int imageNum) {
+ if (_init)
+ unload();
+
+ // build string
+ char number[8];
+ sprintf(number, "%d", imageNum);
+ Common::String imageNameStr(imageName);
+ Common::String specificImageName = imageNameStr + Common::String(number) + Common::String(".png");
+
+ // search for image file
+ if (!SearchMan.hasFile(specificImageName)) {
+ PSP_ERROR("file %s not found\n", specificImageName.c_str());
+ return false;
+ }
+
+ Common::ScopedPtr<Common::SeekableReadStream> file(SearchMan.createReadStreamForMember(specificImageName));
+
+ _buffer = new Buffer();
+ _palette = new Palette();
+ _renderer = new GuRenderer();
+
+ assert(_buffer);
+ assert(_palette);
+ assert(_renderer);
+
+ // Load a PNG into our buffer and palette. Size it by the actual size of the image
+ PngLoader image(file, *_buffer, *_palette, Buffer::kSizeBySourceSize);
+
+ PngLoader::Status status = image.allocate(); // allocate the buffers for the file
+
+ char error[100];
+ if (status == PngLoader::BAD_FILE) {
+ sprintf(error, "Cannot display %s. Not a proper PNG file", specificImageName.c_str());
+ GUI::TimedMessageDialog dialog(error, 4000);
+ dialog.runModal();
+ return false;
+ } else if (status == PngLoader::OUT_OF_MEMORY) {
+ sprintf(error, "Out of memory loading %s. Try making the image smaller", specificImageName.c_str());
+ GUI::TimedMessageDialog dialog(error, 4000);
+ dialog.runModal();
+ return false;
+ }
+ // try to load the image file
+ if (!image.load()) {
+ sprintf(error, "Cannot display %s. Not a proper PNG file", specificImageName.c_str());
+ GUI::TimedMessageDialog dialog(error, 4000);
+ dialog.runModal();
+ return false;
+ }
+
+ setConstantRendererOptions();
+ setFullScreenImageParams(); // prepare renderer for full screen view
+
+ _imageNum = imageNum; // now we can say we displayed this image
+ _init = true;
+
+ return true;
+}
+
+void ImageViewer::setConstantRendererOptions() {
+ _renderer->setBuffer(_buffer);
+ _renderer->setPalette(_palette);
+
+ _renderer->setAlphaBlending(false);
+ _renderer->setColorTest(false);
+ _renderer->setUseGlobalScaler(false);
+ _renderer->setStretch(true);
+ _renderer->setOffsetInBuffer(0, 0);
+ _renderer->setDrawWholeBuffer();
+}
+
+void ImageViewer::unload() {
+ _init = false;
+ delete _buffer;
+ delete _palette;
+ delete _renderer;
+ _buffer = 0;
+ _palette = 0;
+ _renderer = 0;
+}
+
+void ImageViewer::resetOnEngineDone() {
+ _imageNum = 0;
+}
+
+void ImageViewer::setVisible(bool visible) {
+ DEBUG_ENTER_FUNC();
+
+ if (_visible == visible)
+ return;
+
+ // from here on, we're making the loader visible
+ if (visible && g_engine) { // we can only run the image viewer when there's an engine
+ g_engine->pauseEngine(true);
+
+ load(_imageNum ? _imageNum : 1); // load the 1st image or the current
+ }
+
+ if (visible && _init) { // we managed to load
+ _visible = true;
+ setViewerButtons(true);
+
+ { // so dialog goes out of scope, destroying all allocations
+ GUI::TimedMessageDialog dialog("Image Viewer", 1000);
+ dialog.runModal();
+ }
+
+ runLoop(); // only listen to viewer events
+ } else { // we were asked to make invisible or failed to load
+ _visible = false;
+ unload();
+ setViewerButtons(false);
+
+ if (g_engine && g_engine->isPaused())
+ g_engine->pauseEngine(false);
+ }
+ setDirty();
+}
+
+// This is the only way we can truly pause the games
+// Sad but true.
+void ImageViewer::runLoop() {
+ while (_visible) {
+ Common::Event event;
+ PspThread::delayMillis(30);
+ _inputHandler->getAllInputs(event);
+ _displayManager->renderAll();
+ }
+}
+
+void ImageViewer::setViewerButtons(bool active) {
+ _inputHandler->setImageViewerMode(active);
+}
+
+void ImageViewer::loadNextImage() {
+ if (!load(_imageNum+1)) { // try to load the next image
+ if (!load(_imageNum)) // we failed, so reload the current image
+ setVisible(false); // just hide
+ }
+ setDirty();
+}
+
+void ImageViewer::loadLastImage() {
+ if (_imageNum - 1 > 0) {
+ if (!load(_imageNum-1))
+ if (!load(_imageNum))
+ setVisible(false); // we can't even show the old image so hide
+ }
+ setDirty();
+}
+
+void ImageViewer::setFullScreenImageParams() {
+ // we try to fit the image fullscreen at least in one dimension
+ uint32 width = _buffer->getSourceWidth();
+ uint32 height = _buffer->getSourceHeight();
+
+ _centerX = PSP_SCREEN_WIDTH / 2.0f;
+ _centerY = PSP_SCREEN_HEIGHT / 2.0f;
+
+ // see if we fit width wise
+ if (PSP_SCREEN_HEIGHT >= (int)((height * PSP_SCREEN_WIDTH) / (float)width)) {
+ setZoom(PSP_SCREEN_WIDTH / (float)width);
+ } else {
+ setZoom(PSP_SCREEN_HEIGHT / (float)height);
+ }
+}
+
+void ImageViewer::render() {
+ if (_init) {
+ assert(_buffer);
+ assert(_renderer);
+
+ // move the image slightly. Note that we count on the renderer's timing
+ switch (_movement) {
+ case EVENT_MOVE_LEFT:
+ moveImageX(-_visibleWidth / 100.0f);
+ break;
+ case EVENT_MOVE_UP:
+ moveImageY(-_visibleHeight / 100.0f);
+ break;
+ case EVENT_MOVE_RIGHT:
+ moveImageX(_visibleWidth / 100.0f);
+ break;
+ case EVENT_MOVE_DOWN:
+ moveImageY(_visibleHeight / 100.0f);
+ break;
+ default:
+ break;
+ }
+ _renderer->render();
+ }
+}
+
+void ImageViewer::modifyZoom(bool up) {
+ float factor = _zoomFactor;
+ if (up)
+ factor += 0.1f;
+ else // down
+ factor -= 0.1f;
+
+ setZoom(factor);
+}
+
+void ImageViewer::setZoom(float value) {
+ if (value <= 0.0f) // don't want 0 or negative zoom
+ return;
+
+ _zoomFactor = value;
+ _renderer->setStretchXY(value, value);
+ setOffsetParams();
+}
+
+void ImageViewer::moveImageX(float val) {
+ float newVal = _centerX + val;
+
+ if (newVal - (_visibleWidth / 2) > PSP_SCREEN_WIDTH - 4 || newVal + (_visibleWidth / 2) < 4)
+ return;
+ _centerX = newVal;
+ setOffsetParams();
+}
+
+void ImageViewer::moveImageY(float val) {
+ float newVal = _centerY + val;
+
+ if (newVal - (_visibleHeight / 2) > PSP_SCREEN_HEIGHT - 4 || newVal + (_visibleHeight / 2) < 4)
+ return;
+ _centerY = newVal;
+ setOffsetParams();
+}
+
+// Set the renderer with the proper offset on the screen
+//
+void ImageViewer::setOffsetParams() {
+ _visibleWidth = _zoomFactor * _buffer->getSourceWidth();
+ _visibleHeight = _zoomFactor * _buffer->getSourceHeight();
+
+ int offsetX = _centerX - (int)(_visibleWidth * 0.5f);
+ int offsetY = _centerY - (int)(_visibleHeight * 0.5f);
+
+ _renderer->setOffsetOnScreen(offsetX, offsetY);
+ setDirty();
+}
+
+// Handler events coming in from the inputHandler
+//
+void ImageViewer::handleEvent(uint32 event) {
+ DEBUG_ENTER_FUNC();
+
+ switch (event) {
+ case EVENT_HIDE:
+ setVisible(false);
+ break;
+ case EVENT_SHOW:
+ setVisible(true);
+ break;
+ case EVENT_ZOOM_IN:
+ modifyZoom(true);
+ break;
+ case EVENT_ZOOM_OUT:
+ modifyZoom(false);
+ break;
+ case EVENT_MOVE_LEFT:
+ case EVENT_MOVE_UP:
+ case EVENT_MOVE_RIGHT:
+ case EVENT_MOVE_DOWN:
+ case EVENT_MOVE_STOP:
+ _movement = (Event)event;
+ break;
+ case EVENT_NEXT_IMAGE:
+ loadNextImage();
+ break;
+ case EVENT_LAST_IMAGE:
+ loadLastImage();
+ break;
+ default:
+ PSP_ERROR("Unknown event %d\n", event);
+ break;
+ }
+} \ No newline at end of file
diff --git a/backends/platform/psp/image_viewer.h b/backends/platform/psp/image_viewer.h
new file mode 100644
index 0000000000..ef8b196dbe
--- /dev/null
+++ b/backends/platform/psp/image_viewer.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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $
+ * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $
+ *
+ */
+
+#ifndef PSP_IMAGE_VIEWER_H
+#define PSP_IMAGE_VIEWER_H
+
+class InputHandler;
+
+class ImageViewer : public DisplayClient {
+public:
+ enum Event {
+ EVENT_NONE = -1,
+ EVENT_HIDE = 0,
+ EVENT_SHOW = 1,
+ EVENT_ZOOM_IN,
+ EVENT_ZOOM_OUT,
+ EVENT_MOVE_LEFT,
+ EVENT_MOVE_UP,
+ EVENT_MOVE_RIGHT,
+ EVENT_MOVE_DOWN,
+ EVENT_MOVE_STOP,
+ EVENT_NEXT_IMAGE,
+ EVENT_LAST_IMAGE,
+ };
+
+private:
+ Buffer *_buffer;
+ Palette *_palette;
+ GuRenderer *_renderer;
+ bool _visible;
+ bool _dirty;
+ bool _init;
+ uint32 _imageNum; // current image number
+ float _zoomFactor; // how much we're zooming in/out on the image
+ float _visibleHeight, _visibleWidth;
+ float _centerX, _centerY;
+ Event _movement;
+
+ InputHandler *_inputHandler;
+ DisplayManager *_displayManager;
+
+ void setFullScreenImageParams();
+ void loadNextImage();
+ void loadLastImage();
+ void setViewerButtons(bool active);
+ void setConstantRendererOptions();
+ void moveImageX(float val);
+ void moveImageY(float val);
+ bool load(int imageNum);
+ void unload();
+ void runLoop(); // to get total pausing we have to do our own loop
+
+ void setZoom(float value);
+ void setOffsetParams();
+ void modifyZoom(bool up); // up or down
+ void setVisible(bool visible);
+
+public:
+
+ ImageViewer() : _buffer(0), _palette(0), _visible(false),
+ _dirty(false), _init(false), _imageNum(0),
+ _zoomFactor(0.0f), _visibleHeight(0.0f), _visibleWidth(0.0f),
+ _centerX(0.0f), _centerY(0.0f), _movement(EVENT_MOVE_STOP),
+ _inputHandler(0), _displayManager(0) {}
+ ~ImageViewer() { unload(); } // deallocate images
+ bool load();
+ void render();
+ bool isVisible() { return _visible; }
+ bool isDirty() { return _dirty; }
+ void setDirty() { _dirty = true; }
+ void setClean() { if (!_visible) // otherwise we want to keep rendering
+ _dirty = false;
+ }
+ void resetOnEngineDone();
+
+ void handleEvent(uint32 event);
+
+ // pointer setters
+ void setInputHandler(InputHandler *inputHandler) { _inputHandler = inputHandler; }
+ void setDisplayManager(DisplayManager *displayManager) { _displayManager = displayManager; }
+};
+
+#endif /* PSP_IMAGE_VIEWER_H */ \ No newline at end of file
diff --git a/backends/platform/psp/input.cpp b/backends/platform/psp/input.cpp
index 2a91ce455a..665f41504f 100644
--- a/backends/platform/psp/input.cpp
+++ b/backends/platform/psp/input.cpp
@@ -23,238 +23,274 @@
*
*/
-// Todo: handle events that should fire because of shift going off
-// Solution: handle shift on a button-by-button basis, only allowing it when the button is up. Also a inputmap-wide button. At buttonup, shiftstate is inspected per button.
+#include <pspctrl.h>
+#include "gui/message.h"
+#include "backends/platform/psp/input.h"
//#define __PSP_DEBUG_FUNCS__ /* Uncomment for debugging the stack */
//#define __PSP_DEBUG_PRINT__ /* Uncomment for debug prints */
-
#include "backends/platform/psp/trace.h"
-#include "backends/platform/psp/psppixelformat.h"
-#include "backends/platform/psp/input.h"
-
// Defines for working with PSP buttons
-#define CHANGED(x) (_buttonsChanged & (x))
-#define PRESSED(x) ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
-#define UNPRESSED(x) ((_buttonsChanged & (x)) && !(pad.Buttons & (x)))
-#define DOWN(x) (pad.Buttons & (x))
+#define DOWN(x) ((pad.Buttons & (x)) == (x))
#define UP(x) (!(pad.Buttons & (x)))
#define PSP_DPAD (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
#define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)
#define PSP_TRIGGERS (PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER)
+#define PSP_ALL_BUTTONS (PSP_DPAD | PSP_4BUTTONS | PSP_TRIGGERS | PSP_CTRL_START | PSP_CTRL_SELECT)
#define PAD_CHECK_TIME 53
-void InputHandler::init() {
- sceCtrlSetSamplingCycle(0); // set sampling to vsync. n = n usecs
- sceCtrlSetSamplingMode(1); // analog
+Button::Button() {
+ clear();
}
-bool InputHandler::getAllInputs(Common::Event &event) {
- DEBUG_ENTER_FUNC();
+inline void Button::clear() {
+ _key = Common::KEYCODE_INVALID;
+ _ascii = 0;
+ _flag = 0;
+ _pspEventDown.clear();
+ _pspEventUp.clear();
+}
- uint32 time = g_system->getMillis(); // may not be necessary with read
- if (time - _lastPadCheckTime < PAD_CHECK_TIME) {
- return false;
+inline bool Button::getEvent(Common::Event &event, PspEvent &pspEvent, bool down) {
+ if (down) {
+ if (!_pspEventDown.isEmpty())
+ pspEvent = _pspEventDown;
+ } else { // up
+ if (!_pspEventUp.isEmpty())
+ pspEvent = _pspEventUp;
}
+ if (_key != Common::KEYCODE_INVALID) {
+ event.type = down ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
+ event.kbd.keycode = _key;
+ event.kbd.ascii = _ascii;
+ event.kbd.flags |= _flag;
+ return true;
+ } else if (_flag) { // handle flag only events
+ event.type = down ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
+ event.kbd.flags |= down ? _flag : 0;
+ return true;
+ }
+ return false;
+}
- _lastPadCheckTime = time;
- SceCtrlData pad;
-
- sceCtrlPeekBufferPositive(&pad, 1); // Peek ignores sleep. Read sleeps thread
-
- bool haveEvent;
-
- memset(&event, 0, sizeof(event));
+void Button::setPspEvent(PspEventType typeDown, uint32 dataDown, PspEventType typeUp, uint32 dataUp) {
+ _pspEventDown.type = typeDown;
+ _pspEventDown.data = dataDown;
+ _pspEventUp.type = typeUp;
+ _pspEventUp.data = dataUp;
+}
- if (_keyboard->isVisible())
- haveEvent = _keyboard->processInput(event, pad);
- else
- haveEvent = getEvent(event, pad);
+// Translates bitfields to our constants
+// We put combined bitfields first to make sure we pick up diagonals
+const uint32 ButtonPad::_buttonMap[] = {
+ PSP_CTRL_UP | PSP_CTRL_LEFT,
+ PSP_CTRL_UP | PSP_CTRL_RIGHT,
+ PSP_CTRL_DOWN | PSP_CTRL_RIGHT,
+ PSP_CTRL_DOWN | PSP_CTRL_LEFT,
+ PSP_CTRL_RIGHT, PSP_CTRL_DOWN, PSP_CTRL_LEFT, PSP_CTRL_UP,
+ PSP_CTRL_CROSS, PSP_CTRL_CIRCLE, PSP_CTRL_TRIANGLE, PSP_CTRL_SQUARE,
+ PSP_CTRL_LTRIGGER, PSP_CTRL_RTRIGGER, PSP_CTRL_START, PSP_CTRL_SELECT
+};
+
+ButtonPad::ButtonPad() : _prevButtonState(0), _shifted(UNSHIFTED), _padMode(PAD_MODE_NORMAL),
+ _comboMode(false), _combosEnabled(true) {
+ for (int i = UNSHIFTED; i < SHIFTED_MODE_LAST; i++)
+ _buttonsChanged[i] = 0;
+ clearButtons();
+}
- if (haveEvent) {
- PSP_DEBUG_PRINT("Have event[%s]\n", haveEvent ? "true" : "false");
- PSP_DEBUG_PRINT("event.type[%d]\n", event.type);
+void ButtonPad::clearButtons() {
+ for (int i = BTN_UP_LEFT; i < BTN_LAST; i++) {
+ _button[i][UNSHIFTED].clear();
+ _button[i][SHIFTED].clear();
}
+}
- return haveEvent;
+void ButtonPad::initButtons() {
+ switch (_padMode) {
+ case PAD_MODE_NORMAL:
+ initButtonsNormalMode();
+ break;
+ case PAD_MODE_LOL:
+ initButtonsLolMode();
+ break;
+ default:
+ break;
+ }
}
-bool InputHandler::getEvent(Common::Event &event, SceCtrlData &pad) {
+void ButtonPad::initButtonsNormalMode() {
DEBUG_ENTER_FUNC();
+ PSP_DEBUG_PRINT("initializing buttons for normal mode\n");
+ clearButtons();
+
+ // Dpad
+ _button[BTN_UP_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP7, '7');
+ _button[BTN_LEFT][SHIFTED].setKey(Common::KEYCODE_KP7, '7'); // same as up_left
+ _button[BTN_UP][UNSHIFTED].setKey(Common::KEYCODE_KP8, '8');
+ _button[BTN_UP_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP9, '9');
+ _button[BTN_UP][SHIFTED].setKey(Common::KEYCODE_KP9, '9'); // same as up_right
+ _button[BTN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP4, '4');
+ _button[BTN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP6, '6');
+ _button[BTN_DOWN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP1, '1');
+ _button[BTN_DOWN][SHIFTED].setKey(Common::KEYCODE_KP1, '1'); // same as down_left
+ _button[BTN_DOWN][UNSHIFTED].setKey(Common::KEYCODE_KP2, '2');
+ _button[BTN_DOWN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP3, '3');
+ _button[BTN_RIGHT][SHIFTED].setKey(Common::KEYCODE_KP3, '3'); // same as down_right
+
+ // Other buttons
+ _button[BTN_CROSS][UNSHIFTED].setPspEvent(PSP_EVENT_LBUTTON, true, PSP_EVENT_LBUTTON, false);
+ _button[BTN_CIRCLE][UNSHIFTED].setPspEvent(PSP_EVENT_RBUTTON, true, PSP_EVENT_RBUTTON, false);
+ _button[BTN_TRIANGLE][UNSHIFTED].setKey(Common::KEYCODE_RETURN, '\r');
+ _button[BTN_SQUARE][UNSHIFTED].setKey(Common::KEYCODE_PERIOD, '.');
+ _button[BTN_SQUARE][SHIFTED].setKey(Common::KEYCODE_SPACE, ' ');
+ _button[BTN_LTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_ESCAPE, 27);
+ _button[BTN_RTRIGGER][SHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
+ _button[BTN_RTRIGGER][UNSHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
+ _button[BTN_RTRIGGER][SHIFTED].setKey(Common::KEYCODE_INVALID, 0, Common::KBD_SHIFT);
+ _button[BTN_RTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_INVALID, 0, Common::KBD_SHIFT);
+ _button[BTN_START][SHIFTED].setKey(Common::KEYCODE_F5, Common::ASCII_F5);
+ _button[BTN_START][UNSHIFTED].setKey(Common::KEYCODE_F5, Common::ASCII_F5, Common::KBD_CTRL);
+ _button[BTN_SELECT][UNSHIFTED].setPspEvent(PSP_EVENT_SHOW_VIRTUAL_KB, true, PSP_EVENT_NONE, 0);
+ _button[BTN_SELECT][SHIFTED].setPspEvent(PSP_EVENT_IMAGE_VIEWER, true, PSP_EVENT_NONE, 0);
+}
- _buttonsChanged = pad.Buttons ^ _prevButtons;
- bool haveEvent = false;
+void ButtonPad::initButtonsLolMode() {
+ DEBUG_ENTER_FUNC();
+ initButtonsNormalMode(); // set normal button configuration
+ PSP_DEBUG_PRINT("initializing buttons for LOL mode\n");
+
+ // Square is our new shift button
+ _button[BTN_SQUARE][UNSHIFTED].clear();
+ _button[BTN_SQUARE][UNSHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
+ _button[BTN_SQUARE][SHIFTED].clear();
+ _button[BTN_SQUARE][SHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
+
+ // Dpad
+ _button[BTN_LEFT][UNSHIFTED].clear();
+ _button[BTN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP7, '7');
+ _button[BTN_LEFT][SHIFTED].clear();
+ _button[BTN_LEFT][SHIFTED].setKey(Common::KEYCODE_F1, Common::ASCII_F1);
+ _button[BTN_UP][SHIFTED].clear();
+ _button[BTN_UP][SHIFTED].setKey(Common::KEYCODE_F2, Common::ASCII_F2);
+ _button[BTN_RIGHT][UNSHIFTED].clear();
+ _button[BTN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP9, '9');
+ _button[BTN_RIGHT][SHIFTED].clear();
+ _button[BTN_RIGHT][SHIFTED].setKey(Common::KEYCODE_F3, Common::ASCII_F3);
+ _button[BTN_DOWN][SHIFTED].clear();
+ _button[BTN_DOWN][SHIFTED].setKey(Common::KEYCODE_F4, Common::ASCII_F4);
+
+ // Buttons
+ _button[BTN_LTRIGGER][UNSHIFTED].clear();
+ _button[BTN_LTRIGGER][SHIFTED].clear();
+ _button[BTN_LTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_KP4, '4'); // Triggers turn
+ _button[BTN_RTRIGGER][UNSHIFTED].clear();
+ _button[BTN_RTRIGGER][SHIFTED].clear();
+ _button[BTN_RTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_KP6, '6');
+ _button[BTN_START][SHIFTED].clear();
+ _button[BTN_START][SHIFTED].setKey(Common::KEYCODE_ESCAPE, 27);
+}
- // Collect events from different sources
- haveEvent = getDpadEvent(event, pad);
+bool ButtonPad::getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
+ DEBUG_ENTER_FUNC();
- if (!haveEvent)
- haveEvent = getButtonEvent(event, pad);
+ //PSP_DEBUG_PRINT("buttons[%x]\n", pad.Buttons);
- if (!haveEvent)
- haveEvent = getNubEvent(event, pad);
+ uint32 curButtonState = PSP_ALL_BUTTONS & pad.Buttons; // we only care about these
- _prevButtons = pad.Buttons;
+ if (_combosEnabled)
+ modifyButtonsForCombos(pad); // change buttons for combos
- return haveEvent;
+ return getEventFromButtonState(event, pspEvent, curButtonState);
}
-bool InputHandler::getDpadEvent(Common::Event &event, SceCtrlData &pad) {
+bool ButtonPad::getEventFromButtonState(Common::Event &event, PspEvent &pspEvent, uint32 buttonState) {
DEBUG_ENTER_FUNC();
+ _buttonsChanged[_shifted] |= buttonState ^ _prevButtonState; // add any buttons that changed
+ _prevButtonState = buttonState;
+
+ for (int shiftState = UNSHIFTED; shiftState < SHIFTED_MODE_LAST; shiftState++) {
+ if (_buttonsChanged[shiftState]) { // any button to address?
+ PSP_DEBUG_PRINT("found changed buttons\n");
+ ButtonType buttonType = BTN_LAST;
+ bool buttonDown = false; // normally we release a button (as in when we're in a different shiftmode)
+
+ for (int i = BTN_UP_LEFT; i < BTN_LAST; i++) {
+ uint32 buttonCode = _buttonMap[i];
+ if ((_buttonsChanged[shiftState] & buttonCode) == buttonCode) { // check for this changed button
+ buttonType = (ButtonType)i; // we know which button changed
+ _buttonsChanged[shiftState] &= ~buttonCode; // save the fact that we treated this button
+ if (shiftState == _shifted)
+ buttonDown = buttonState & buttonCode ? true : false; // pressed or released?
+
+ PSP_DEBUG_PRINT("button[%i] pressed\n", i);
+ break;
+ }
+ }
- int newDpadX = 0, newDpadY = 0;
- bool haveEvent = false;
-
- if (DOWN(PSP_CTRL_UP)) {
- newDpadY++;
- if (DOWN(PSP_CTRL_RTRIGGER)) // Shifting causes diagonals
- newDpadX++;
- }
- if (DOWN(PSP_CTRL_RIGHT)) {
- newDpadX++;
- if (DOWN(PSP_CTRL_RTRIGGER))
- newDpadY--;
- }
- if (DOWN(PSP_CTRL_DOWN)) {
- newDpadY--;
- if (DOWN(PSP_CTRL_RTRIGGER))
- newDpadX--;
- }
- if (DOWN(PSP_CTRL_LEFT)) {
- newDpadX--;
- if (DOWN(PSP_CTRL_RTRIGGER))
- newDpadY++;
- }
-
- if (newDpadX != _dpadX || newDpadY != _dpadY) {
- if (_dpadX == 0 && _dpadY == 0) { // We were in the middle so we pressed dpad
- event.type = Common::EVENT_KEYDOWN;
- event.kbd.keycode = translateDpad(newDpadX, newDpadY);
- event.kbd.ascii = event.kbd.keycode - Common::KEYCODE_KP0 + '0'; // Get ascii
- _dpadX = newDpadX;
- _dpadY = newDpadY;
- } else if (newDpadX == 0 && newDpadY == 0) {// We're now centered so we unpressed dpad
- event.type = Common::EVENT_KEYUP;
- event.kbd.keycode = translateDpad(_dpadX, _dpadY);
- event.kbd.ascii = event.kbd.keycode - Common::KEYCODE_KP0 + '0';
- _dpadX = newDpadX;
- _dpadY = newDpadY;
- } else { // we moved from one pressed dpad direction to another one
- event.type = Common::EVENT_KEYUP; // first release the last dpad direction
- event.kbd.keycode = translateDpad(_dpadX, _dpadY);
- event.kbd.ascii = event.kbd.keycode - Common::KEYCODE_KP0 + '0';
- _dpadX = 0; // so that we'll pick up a new dpad movement the next round
- _dpadY = 0;
+ assert (buttonType < BTN_LAST);
+ bool haveEvent = _button[buttonType][shiftState].getEvent(event, pspEvent, buttonDown);
+ if (haveEvent)
+ PSP_DEBUG_PRINT("have event. key[%d] flag[%x] %s\n", event.kbd.ascii, event.kbd.flags, buttonDown ? "down" : "up");
+ return haveEvent;
}
-
- PSP_DEBUG_PRINT("Keypad event. DpadX[%d], DpadY[%d]\n", _dpadX, _dpadY);
- haveEvent = true;
}
- return haveEvent;
+ return false;
}
-inline Common::KeyCode InputHandler::translateDpad(int x, int y) {
- DEBUG_ENTER_FUNC();
- Common::KeyCode key;
-
- if (x == -1) {
- if (y == -1)
- key = Common::KEYCODE_KP1;
- else if (y == 0)
- key = Common::KEYCODE_KP4;
- else /* y == 1 */
- key = Common::KEYCODE_KP7;
- } else if (x == 0) {
- if (y == -1)
- key = Common::KEYCODE_KP2;
- else /* y == 1 */
- key = Common::KEYCODE_KP8;
- } else {/* x == 1 */
- if (y == -1)
- key = Common::KEYCODE_KP3;
- else if (y == 0)
- key = Common::KEYCODE_KP6;
- else /* y == 1 */
- key = Common::KEYCODE_KP9;
+void ButtonPad::modifyButtonsForCombos(SceCtrlData &pad) {
+ if (DOWN(PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) {
+ if (!_comboMode) { // we're entering combo mode
+ PSP_DEBUG_PRINT("entering combo mode\n");
+ _button[BTN_SQUARE][UNSHIFTED].clear();
+ _button[BTN_SQUARE][SHIFTED].clear();
+ _button[BTN_DOWN][SHIFTED].clear();
+ _button[BTN_DOWN][UNSHIFTED].clear();
+ _button[BTN_UP][SHIFTED].clear();
+ _button[BTN_UP][UNSHIFTED].clear();
+ _button[BTN_SQUARE][UNSHIFTED].setPspEvent(PSP_EVENT_MODE_SWITCH, true, PSP_EVENT_NONE, true);
+ _button[BTN_SQUARE][SHIFTED].setPspEvent(PSP_EVENT_MODE_SWITCH, true, PSP_EVENT_NONE, true);
+ _button[BTN_DOWN][UNSHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, false, PSP_EVENT_NONE, true);
+ _button[BTN_DOWN][SHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, false, PSP_EVENT_NONE, true);
+ _button[BTN_UP][UNSHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, true, PSP_EVENT_NONE, true);
+ _button[BTN_UP][SHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, true, PSP_EVENT_NONE, true);
+ _comboMode = true;
+ }
+ } else { // no combo buttons are pressed now
+ if (_comboMode) { // we have been running in combo mode
+ initButtons(); // reset the button configuration
+ _comboMode = false;
+ }
}
-
- return key;
}
-
-bool InputHandler::getButtonEvent(Common::Event &event, SceCtrlData &pad) {
+bool Nub::getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
- bool haveEvent = false;
-
- if (PRESSED(PSP_CTRL_SELECT))
- _keyboard->setVisible(true);
-
- else if (CHANGED(PSP_4BUTTONS | PSP_TRIGGERS | PSP_CTRL_START)) {
- if (CHANGED(PSP_CTRL_CROSS)) {
- event.type = DOWN(PSP_CTRL_CROSS) ? Common::EVENT_LBUTTONDOWN : Common::EVENT_LBUTTONUP;
- event.mouse.x = _cursor->getX(); // Could this have to do with SCI enter problem?
- event.mouse.y = _cursor->getY();
- PSP_DEBUG_PRINT("%s\n", event.type == Common::EVENT_LBUTTONDOWN ? "LButtonDown" : "LButtonUp");
- } else if (CHANGED(PSP_CTRL_CIRCLE)) {
- event.type = DOWN(PSP_CTRL_CIRCLE) ? Common::EVENT_RBUTTONDOWN : Common::EVENT_RBUTTONUP;
- event.mouse.x = _cursor->getX();
- event.mouse.y = _cursor->getY();
- PSP_DEBUG_PRINT("%s\n", event.type == Common::EVENT_LBUTTONDOWN ? "RButtonDown" : "RButtonUp");
- } else {
- //any of the other buttons.
- event.type = _buttonsChanged & pad.Buttons ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
- event.kbd.ascii = 0;
- event.kbd.flags = 0;
-
- if (CHANGED(PSP_CTRL_LTRIGGER)) {
- event.kbd.keycode = Common::KEYCODE_ESCAPE;
- event.kbd.ascii = 27;
- } else if (CHANGED(PSP_CTRL_START)) {
- event.kbd.keycode = Common::KEYCODE_F5;
- event.kbd.ascii = Common::ASCII_F5;
- if (DOWN(PSP_CTRL_RTRIGGER)) {
- event.kbd.flags |= Common::KBD_CTRL; // Main menu to allow RTL
- }
- } else if (CHANGED(PSP_CTRL_SQUARE)) {
- event.kbd.keycode = Common::KEYCODE_PERIOD;
- event.kbd.ascii = '.';
- } else if (CHANGED(PSP_CTRL_TRIANGLE)) {
- event.kbd.keycode = Common::KEYCODE_RETURN;
- event.kbd.ascii = '\r';
- } else if (DOWN(PSP_CTRL_RTRIGGER)) { // An event
- event.kbd.flags |= Common::KBD_SHIFT;
- }
- PSP_DEBUG_PRINT("Ascii[%d]. Key %s.\n", event.kbd.ascii, event.type == Common::EVENT_KEYDOWN ? "down" : "up");
- }
- haveEvent = true;
+ if (_dpadMode) { // Convert the nub to a D-Pad
+ uint32 buttonState;
+ translateToDpadState(pad.Lx, pad.Ly, buttonState);
+ return _buttonPad.getEventFromButtonState(event, pspEvent, buttonState);
}
- return haveEvent;
-}
-
-bool InputHandler::getNubEvent(Common::Event &event, SceCtrlData &pad) {
- DEBUG_ENTER_FUNC();
-
- bool haveEvent = false;
int32 analogStepX = pad.Lx; // Goes up to 255.
int32 analogStepY = pad.Ly;
- int32 oldX = _cursor->getX();
- int32 oldY = _cursor->getY();
-
analogStepX = modifyNubAxisMotion(analogStepX);
analogStepY = modifyNubAxisMotion(analogStepY);
+ int32 oldX = _cursor->getX();
+ int32 oldY = _cursor->getY();
+
if (analogStepX != 0 || analogStepY != 0) {
PSP_DEBUG_PRINT("raw x[%d], y[%d]\n", analogStepX, analogStepY);
// If no movement then this has no effect
- if (DOWN(PSP_CTRL_RTRIGGER)) {
+ if (_shifted) {
// Fine control mode for analog
if (analogStepX != 0) {
if (analogStepX > 0)
@@ -281,15 +317,29 @@ bool InputHandler::getNubEvent(Common::Event &event, SceCtrlData &pad) {
event.type = Common::EVENT_MOUSEMOVE;
event.mouse.x = newX;
event.mouse.y = newY;
- haveEvent = true;
-
PSP_DEBUG_PRINT("Nub event. X[%d], Y[%d]\n", newX, newY);
+ return true;
}
}
- return haveEvent;
+ return false;
+}
+
+void Nub::translateToDpadState(int dpadX, int dpadY, uint32 &buttonState) {
+ #define MIN_NUB_POSITION 70
+ buttonState = 0;
+
+ if (dpadX > 127 + MIN_NUB_POSITION)
+ buttonState |= PSP_CTRL_RIGHT;
+ else if (dpadX < 127 - MIN_NUB_POSITION)
+ buttonState |= PSP_CTRL_LEFT;
+
+ if (dpadY > 127 + MIN_NUB_POSITION)
+ buttonState |= PSP_CTRL_DOWN;
+ else if (dpadY < 127 - MIN_NUB_POSITION)
+ buttonState |= PSP_CTRL_UP;
}
-inline int32 InputHandler::modifyNubAxisMotion(int32 input) {
+inline int32 Nub::modifyNubAxisMotion(int32 input) {
DEBUG_ENTER_FUNC();
const int MIN_NUB_MOTION = 30;
@@ -304,3 +354,220 @@ inline int32 InputHandler::modifyNubAxisMotion(int32 input) {
return input;
}
+
+inline bool Nub::isButtonDown() {
+ if (_dpadMode) // only relevant in dpad mode
+ return _buttonPad.isButtonDown();
+ return false;
+}
+
+const char *InputHandler::_padModeText[] = {
+ "Normal Button Mode",
+ "1st Person RPG Button Mode"
+};
+
+void InputHandler::init() {
+ sceCtrlSetSamplingCycle(0); // set sampling to vsync. n = n usecs
+ sceCtrlSetSamplingMode(1); // analog
+
+ _buttonPad.initButtons();
+ _nub.init();
+}
+
+bool InputHandler::getAllInputs(Common::Event &event) {
+ DEBUG_ENTER_FUNC();
+
+ uint32 time = g_system->getMillis(); // may not be necessary with read
+ if (time - _lastPadCheckTime < PAD_CHECK_TIME) {
+ return false;
+ }
+
+ _lastPadCheckTime = time;
+ SceCtrlData pad;
+
+ sceCtrlPeekBufferPositive(&pad, 1); // Peek doesn't sleep. Read sleeps the thread
+
+ bool haveEvent;
+ //memset(&event, 0, sizeof(event));
+
+ haveEvent = getEvent(event, pad);
+
+ if (haveEvent) {
+ PSP_DEBUG_PRINT("Have event[%s]. Type[%d]\n", haveEvent ? "true" : "false", event.type);
+ }
+
+ return haveEvent;
+}
+
+bool InputHandler::getEvent(Common::Event &event, SceCtrlData &pad) {
+ DEBUG_ENTER_FUNC();
+
+ PspEvent pspEvent;
+ bool haveEvent = false;
+
+ if (_keyboard->isVisible()) {
+ haveEvent = _keyboard->processInput(event, pspEvent, pad);
+ } else { // only process buttonpad if keyboard invisible
+ haveEvent = _buttonPad.getEvent(event, pspEvent, pad);
+ }
+
+ if (!haveEvent && pspEvent.isEmpty())
+ haveEvent = _nub.getEvent(event, pspEvent, pad);
+
+ // handle any pending PSP events
+ if (!haveEvent && pspEvent.isEmpty()) {
+ if (!_pendingPspEvent.isEmpty()) {
+ pspEvent = _pendingPspEvent;
+ _pendingPspEvent.clear();
+ }
+ }
+
+ // handle any PSP events we might have
+ if (!pspEvent.isEmpty())
+ haveEvent |= handlePspEvent(event, pspEvent); // overrides any event we might have
+
+ return haveEvent;
+}
+
+bool InputHandler::handlePspEvent(Common::Event &event, PspEvent &pspEvent) {
+ bool haveEvent = false;
+
+ PSP_DEBUG_PRINT("have pspEvent[%d] data[%d]\n", pspEvent.type, pspEvent.data);
+
+ switch (pspEvent.type) {
+ case PSP_EVENT_SHIFT:
+ handleShiftEvent((ShiftMode)pspEvent.data);
+ break;
+ case PSP_EVENT_SHOW_VIRTUAL_KB:
+ _keyboard->setVisible((bool)pspEvent.data);
+ if ((pspEvent.data && _keyboard->isVisible()) || !pspEvent.data) // don't change mode if keyboard didn't load
+ _nub.setDpadMode((bool)pspEvent.data); // set nub to keypad/regular mode
+ break;
+ case PSP_EVENT_LBUTTON:
+ haveEvent = true;
+ if (pspEvent.data) // down
+ handleMouseEvent(event, Common::EVENT_LBUTTONDOWN, "LButtonDown");
+ else
+ handleMouseEvent(event, Common::EVENT_LBUTTONUP, "LButtonUp");
+ break;
+ case PSP_EVENT_RBUTTON:
+ haveEvent = true;
+ if (pspEvent.data) // down
+ handleMouseEvent(event, Common::EVENT_RBUTTONDOWN, "RButtonDown");
+ else
+ handleMouseEvent(event, Common::EVENT_RBUTTONUP, "RButtonUp");
+ break;
+ case PSP_EVENT_MODE_SWITCH:
+ handleModeSwitchEvent();
+ break;
+ /*case PSP_EVENT_CHANGE_SPEED:
+ handleSpeedChange(pspEvent.data);
+ break;*/
+ case PSP_EVENT_IMAGE_VIEWER:
+ _imageViewer->handleEvent(pspEvent.data);
+ break;
+ case PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS:
+ setImageViewerMode(pspEvent.data);
+ break;
+ default:
+ PSP_ERROR("Unhandled PSP Event[%d]\n", pspEvent.type);
+ break;
+ }
+
+ return haveEvent;
+}
+
+void InputHandler::handleMouseEvent(Common::Event &event, Common::EventType type, const char *string) {
+ event.type = type;
+ event.mouse.x = _cursor->getX();
+ event.mouse.y = _cursor->getY();
+ PSP_DEBUG_PRINT("%s event, x[%d], y[%d]\n", string, event.mouse.x, event.mouse.y);
+}
+
+void InputHandler::handleShiftEvent(ShiftMode shifted) {
+ _buttonPad.setShifted(shifted);
+ _nub.setShifted(shifted);
+}
+
+void InputHandler::handleModeSwitchEvent() {
+ // check if we can't switch modes right now
+ if (_buttonPad.isButtonDown() || _nub.isButtonDown()) { // can't switch yet
+ PSP_DEBUG_PRINT("postponing mode switch event\n");
+ _pendingPspEvent.type = PSP_EVENT_MODE_SWITCH; // queue it to be done later
+ } else { // we can switch
+ PSP_DEBUG_PRINT("mode switch event\n");
+ _padMode = (PspPadMode)(_padMode + 1);
+ if (_padMode >= PAD_MODE_LAST)
+ _padMode = PAD_MODE_NORMAL;
+
+ GUI::TimedMessageDialog dialog(_padModeText[_padMode], 1500);
+ dialog.runModal();
+
+ _buttonPad.setPadMode(_padMode);
+ _buttonPad.initButtons();
+ }
+}
+
+/*
+void InputHandler::handleSpeedChange(bool up) {
+ char *dialogMsg;
+
+ if (up) {
+ dialogMsg = "
+
+ GUI::TimedMessageDialog dialog(_padModeText[_padMode], 1500);
+ dialog.runModal();
+}*/
+
+void InputHandler::setImageViewerMode(bool active) {
+ if (_buttonPad.isButtonDown() || _nub.isButtonDown()) { // can't switch yet
+ PSP_DEBUG_PRINT("postponing image viewer on event\n");
+ _pendingPspEvent.type = PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS; // queue it to be done later
+ _pendingPspEvent.data = active;
+ } else if (active) {
+ _nub.setDpadMode(true);
+ _buttonPad.enableCombos(false); // disable combos
+ setButtonsForImageViewer();
+ } else { // deactivate
+ _nub.setDpadMode(false);
+ _nub.init();
+ _buttonPad.enableCombos(true); // re-enable combos
+ _buttonPad.initButtons();
+ }
+}
+
+void InputHandler::setButtonsForImageViewer() {
+ DEBUG_ENTER_FUNC();
+
+ // Dpad
+ _buttonPad.clearButtons();
+ _buttonPad.getButton(ButtonPad::BTN_UP, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_ZOOM_IN,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_DOWN, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_ZOOM_OUT,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_LEFT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_LAST_IMAGE,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_RIGHT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_NEXT_IMAGE,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_LTRIGGER, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_RTRIGGER, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_START, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
+ PSP_EVENT_NONE, false);
+ _buttonPad.getButton(ButtonPad::BTN_SELECT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
+ PSP_EVENT_NONE, false);
+
+ //Nub
+ _nub.getPad().clearButtons();
+ _nub.getPad().getButton(ButtonPad::BTN_UP, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_UP,
+ PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
+ _nub.getPad().getButton(ButtonPad::BTN_DOWN, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_DOWN,
+ PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
+ _nub.getPad().getButton(ButtonPad::BTN_LEFT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_LEFT,
+ PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
+ _nub.getPad().getButton(ButtonPad::BTN_RIGHT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_RIGHT,
+ PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
+}
+
+
diff --git a/backends/platform/psp/input.h b/backends/platform/psp/input.h
index cd686d9e02..9a1ab6faab 100644
--- a/backends/platform/psp/input.h
+++ b/backends/platform/psp/input.h
@@ -28,36 +28,175 @@
#include "common/scummsys.h"
#include "common/events.h"
-#include "backends/platform/psp/display_client.h"
-#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "backends/platform/psp/cursor.h"
+#include "backends/platform/psp/image_viewer.h"
#include <pspctrl.h>
+enum PspEventType {
+ PSP_EVENT_NONE = 0,
+ PSP_EVENT_SHIFT,
+ PSP_EVENT_SHOW_VIRTUAL_KB,
+ PSP_EVENT_LBUTTON,
+ PSP_EVENT_RBUTTON,
+ PSP_EVENT_MODE_SWITCH,
+ PSP_EVENT_CHANGE_SPEED,
+ PSP_EVENT_IMAGE_VIEWER,
+ PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS,
+ PSP_EVENT_LAST
+};
+
+struct PspEvent {
+ PspEventType type;
+ uint32 data;
+ PspEvent() { clear(); }
+ void clear() {
+ type = PSP_EVENT_NONE;
+ data = 0;
+ }
+ bool isEmpty() { return type == PSP_EVENT_NONE; }
+};
+
+enum PspPadMode {
+ PAD_MODE_NORMAL,
+ PAD_MODE_LOL,
+ PAD_MODE_LAST
+};
+
+enum ShiftMode {
+ UNSHIFTED = 0,
+ SHIFTED = 1,
+ SHIFTED_MODE_LAST
+};
+
+
+class Button {
+private:
+ Common::KeyCode _key;
+ uint32 _ascii;
+ uint32 _flag;
+ PspEvent _pspEventDown; // event when we press
+ PspEvent _pspEventUp; // event when we release
+public:
+ Button();
+ void clear();
+ bool getEvent(Common::Event &event, PspEvent &pspEvent, bool buttonDown);
+ void setKey(Common::KeyCode key, uint32 ascii = 0, uint32 flag = 0) { _key = key; _ascii = ascii; _flag = flag; }
+ void setPspEvent(PspEventType typeDown, uint32 dataDown, PspEventType typeUp, uint32 dataUp);
+};
+
+class ButtonPad {
+public:
+ enum ButtonType { // must match the buttonMap
+ BTN_UP_LEFT,
+ BTN_UP_RIGHT,
+ BTN_DOWN_RIGHT,
+ BTN_DOWN_LEFT,
+ BTN_RIGHT,
+ BTN_DOWN,
+ BTN_LEFT,
+ BTN_UP,
+ BTN_CROSS,
+ BTN_CIRCLE,
+ BTN_TRIANGLE,
+ BTN_SQUARE,
+ BTN_LTRIGGER,
+ BTN_RTRIGGER,
+ BTN_START,
+ BTN_SELECT,
+ BTN_LAST
+ };
+
+private:
+ Button _button[BTN_LAST][SHIFTED_MODE_LAST];
+ uint32 _buttonsChanged[SHIFTED_MODE_LAST]; // normal and shifted
+ uint32 _prevButtonState;
+ ShiftMode _shifted;
+ PspPadMode _padMode;
+ bool _comboMode; // are we in the middle of combos
+ bool _combosEnabled; // can we do combos
+ static const uint32 _buttonMap[]; // maps the buttons to their values
+
+ void initButtonsNormalMode();
+ void initButtonsLolMode();
+ void modifyButtonsForCombos(SceCtrlData &pad);
+
+public:
+ ButtonPad();
+ void initButtons(); // set the buttons to the mode that's selected
+ void clearButtons(); // empty the buttons of all events
+
+ bool getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad);
+ bool getEventFromButtonState(Common::Event &event, PspEvent &pspEvent, uint32 buttonState);
+
+ void setShifted(ShiftMode shifted) { _shifted = shifted; }
+ void setPadMode(PspPadMode mode) { _padMode = mode; }
+ bool isButtonDown() { return _prevButtonState; }
+
+ void enableCombos(bool value) { _combosEnabled = value; }
+ Button &getButton(ButtonType type, ShiftMode mode) { return _button[type][mode]; }
+};
+
+class Nub {
+private:
+ Cursor *_cursor; // to enable changing/getting cursor position
+
+ ShiftMode _shifted;
+ bool _dpadMode;
+
+ ButtonPad _buttonPad; // private buttonpad for dpad mode
+
+ int32 modifyNubAxisMotion(int32 input);
+ void translateToDpadState(int dpadX, int dpadY, uint32 &buttonState); // convert nub data to dpad data
+public:
+ Nub() : _shifted(UNSHIFTED), _dpadMode(false) { }
+ void init() { _buttonPad.initButtons(); }
+
+ void setCursor(Cursor *cursor) { _cursor = cursor; }
+
+ // setters
+ void setDpadMode(bool active) { _dpadMode = active; }
+ void setShifted(ShiftMode shifted) { _shifted = shifted; }
+
+ // getters
+ bool isButtonDown();
+ bool getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad);
+ ButtonPad &getPad() { return _buttonPad; }
+};
+
class InputHandler {
public:
- InputHandler() : _cursor(0), _keyboard(0), _prevButtons(0), _lastPadCheckTime(0), _buttonsChanged(0), _dpadX(0), _dpadY(0) {}
+ InputHandler() : _keyboard(0), _cursor(0), _imageViewer(0), _padMode(PAD_MODE_NORMAL),
+ _lastPadCheckTime(0) {}
+ // pointer setters
+ void setKeyboard(PSPKeyboard *keyboard) { _keyboard = keyboard; }
+ void setCursor(Cursor *cursor) { _cursor = cursor; _nub.setCursor(cursor); }
+ void setImageViewer(ImageViewer *imageViewer) { _imageViewer = imageViewer; }
void init();
bool getAllInputs(Common::Event &event);
- void setKeyboard(PSPKeyboard *keyboard) { _keyboard = keyboard; }
- void setCursor(Cursor *cursor) { _cursor = cursor; }
+ void setImageViewerMode(bool active);
private:
- Cursor *_cursor;
+ Nub _nub;
+ ButtonPad _buttonPad;
+
+ // Pointers to relevant other classes
PSPKeyboard *_keyboard;
- uint32 _prevButtons;
+ Cursor *_cursor;
+ ImageViewer *_imageViewer;
+
+ PspPadMode _padMode; // whice mode we're in
+ PspEvent _pendingPspEvent; // an event that can't be handled yet
uint32 _lastPadCheckTime;
- uint32 _buttonsChanged;
- int32 _dpadX, _dpadY;
- int32 _accelX, _accelY;
+ static const char *_padModeText[];
bool getEvent(Common::Event &event, SceCtrlData &pad);
- bool getDpadEvent(Common::Event &event, SceCtrlData &pad);
- bool getButtonEvent(Common::Event &event, SceCtrlData &pad);
- bool getNubEvent(Common::Event &event, SceCtrlData &pad);
- int32 modifyNubAxisMotion(int32 input);
- Common::KeyCode translateDpad(int x, int y);
+ bool handlePspEvent(Common::Event &event, PspEvent &pspEvent);
+ void handleMouseEvent(Common::Event &event, Common::EventType type, const char *string);
+ void handleShiftEvent(ShiftMode shifted);
+ void handleModeSwitchEvent();
+ void setButtonsForImageViewer();
};
#endif /* PSP_INPUT_H */
diff --git a/backends/platform/psp/memory.cpp b/backends/platform/psp/memory.cpp
index 29d0482d9a..924ab356e8 100644
--- a/backends/platform/psp/memory.cpp
+++ b/backends/platform/psp/memory.cpp
@@ -25,8 +25,8 @@
#include "common/scummsys.h"
#include "common/singleton.h"
-#include "common/list.h"
#include "backends/platform/psp/psppixelformat.h"
+#define PSP_INCLUDE_SWAP
#include "backends/platform/psp/memory.h"
// Class Copier --------------------------------------------------------------------------
@@ -37,23 +37,6 @@
//#define TEST_MEMORY_COPY
-extern "C" {
-
-#ifdef TEST_MEMORY_COPY /* we won't be able to run in this case b/c of printouts */
-extern void *__real_memcpy(void *dst, void *src, size_t bytes);
-#endif
-
-void *__wrap_memcpy(void *dst, void *src, size_t bytes) {
-#ifdef TEST_MEMORY_COPY /* we won't be able to run in this case */
- return __real_memcpy(dst, src, bytes);
-#else
- PspMemory::fastCopy((byte *)dst, (byte *)src, bytes);
- return dst;
-#endif
-}
-
-}
-
void PspMemory::copy(byte *dst, const byte *src, uint32 bytes) {
DEBUG_ENTER_FUNC();
@@ -66,29 +49,29 @@ void PspMemory::copy(byte *dst, const byte *src, uint32 bytes) {
// align the destination pointer first
uint32 prefixDst = (((uint32)dst) & 0x3);
-
+
if (prefixDst) {
- prefixDst = 4 - prefixDst; // prefix only if we have address % 4 != 0
+ prefixDst = 4 - prefixDst; // prefix only if we have address % 4 != 0
PSP_DEBUG_PRINT("prefixDst[%d]\n", prefixDst);
bytes -= prefixDst; // remember we assume bytes >= 4
-
+
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) { // check if it's worthwhile to continue
copy8(dst, src, bytes + prefixDst);
#ifdef TEST_MEMORY_COPY
testCopy(debugDst, debugSrc, debugBytes);
-#endif
+#endif
return;
}
-
+
while (prefixDst--) {
*dst++ = *src++;
- }
+ }
}
-
+
// check the source pointer alignment now
uint32 alignSrc = (((uint32)src) & 0x3);
-
+
if (alignSrc) { // we'll need to realign our reads
copy32Misaligned((uint32 *)dst, src, bytes, alignSrc);
} else {
@@ -97,104 +80,14 @@ void PspMemory::copy(byte *dst, const byte *src, uint32 bytes) {
#ifdef TEST_MEMORY_COPY
testCopy(debugDst, debugSrc, debugBytes);
-#endif
-}
-
-void PspMemory::testCopy(const byte *debugDst, const byte *debugSrc, uint32 debugBytes) {
-
- bool mismatch = false;
- PSP_INFO_PRINT("testing fastCopy...");
-
- for (uint32 i = 0; i < debugBytes; i++) {
- if (debugDst[i] != debugSrc[i]) {
- if (!mismatch) {
- PSP_INFO_PRINT("**** mismatch in copy! ****\n");
- PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
- mismatch = true;
- }
- PSP_INFO_PRINT("[%d]%x!=%x ", i, debugSrc[i], debugDst[i]);
- }
- }
- if (mismatch) {
- PSP_INFO_PRINT("\n");
- } else {
- PSP_INFO_PRINT("ok\n");
- }
-}
-
-//
-// used to swap red and blue
-void PspMemory::swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
- DEBUG_ENTER_FUNC();
-
-#ifdef TEST_MEMORY_COPY
- uint32 debugBytes = bytes;
- const uint16 *debugDst = dst16, *debugSrc = src16;
#endif
-
- // align the destination pointer first
- uint32 prefixDst = (((uint32)dst16) & 0x3); // for swap, we can only have 2 or 0 as our prefix
-
- if (prefixDst) {
- bytes -= prefixDst; // remember we assume bytes > 4
- *dst16++ = format.swapRedBlue16(*src16++);
-
- if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) { // check if it's worthwhile to continue
- swap16(dst16, src16, bytes, format);
-
-#ifdef TEST_MEMORY_COPY
- testSwap(debugDst, debugSrc, debugBytes, format);
-#endif
- return;
- }
- }
-
- // check the source pointer alignment now
- uint32 alignSrc = (((uint32)src16) & 0x3);
-
- if (alignSrc) { // we'll need to realign our reads
- PSP_DEBUG_PRINT("misaligned copy of %u bytes from %p to %p\n", bytes, src16, dst16);
- swap32Misaligned((uint32 *)dst16, src16, bytes, format);
- } else {
- swap32Aligned((uint32 *)dst16, (const uint32 *)src16, bytes, format);
- }
-
-#ifdef TEST_MEMORY_COPY
- testSwap(debugDst, debugSrc, debugBytes, format);
-#endif
-
-}
-
-void PspMemory::testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format) {
-
- bool mismatch = false;
- PSP_INFO_PRINT("testing fastSwap...");
-
- uint32 shorts = debugBytes >> 1;
-
- for (uint32 i = 0; i < shorts; i++) {
- if (debugDst[i] != format.swapRedBlue16(debugSrc[i])) {
- if (!mismatch) {
- PSP_INFO_PRINT("**** mismatch in swap! ****\n");
- PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
- mismatch = true;
- }
- PSP_INFO_PRINT("[%d]%x!=%x ", i<<1, format.swapRedBlue16(debugSrc[i]), debugDst[i]);
- }
- }
- if (mismatch) {
- PSP_INFO_PRINT("\n");
- } else {
- PSP_INFO_PRINT("ok\n");
- }
}
-
void PspMemory::copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes) {
PSP_DEBUG_PRINT("copy32Aligned(): dst32[%p], src32[%p], bytes[%d]\n", dst32, src32, bytes);
int words8 = bytes >> 5;
-
+
// try blocks of 8 words at a time
if (words8) {
while (words8--) {
@@ -217,11 +110,11 @@ void PspMemory::copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes)
dst32[7] = d;
dst32 += 8;
src32 += 8;
- }
+ }
}
-
+
int words4 = (bytes & 0x1F) >> 4;
-
+
// try blocks of 4 words at a time
if (words4) {
uint32 a, b, c, d;
@@ -236,10 +129,10 @@ void PspMemory::copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes)
dst32 += 4;
src32 += 4;
}
-
+
int bytesLeft = (bytes & 0xF); // only look at bytes left after we did the above
int wordsLeft = bytesLeft >> 2;
-
+
// now just do single words
while (wordsLeft) {
*dst32++ = *src32++;
@@ -252,55 +145,20 @@ void PspMemory::copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes)
byte *dst = (byte *)dst32;
byte *src = (byte *)src32;
-
+
while (bytesLeft--) {
*dst++ = *src++;
}
}
-void PspMemory::swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format) {
- DEBUG_ENTER_FUNC();
- int words4 = bytes >> 4;
-
- // try blocks of 4 words at a time
- while (words4--) {
- uint32 a, b, c, d;
- a = format.swapRedBlue32(src32[0]);
- b = format.swapRedBlue32(src32[1]);
- c = format.swapRedBlue32(src32[2]);
- d = format.swapRedBlue32(src32[3]);
- dst32[0] = a;
- dst32[1] = b;
- dst32[2] = c;
- dst32[3] = d;
- dst32 += 4;
- src32 += 4;
- }
-
- uint32 bytesLeft = bytes & 0xF;
- uint32 words = bytesLeft >> 2;
-
- // now just do words
- while (words--) {
- *dst32++ = format.swapRedBlue32(*src32++);
- }
-
- bytesLeft = bytes & 0x3;
-
- if (bytesLeft) { // for swap, can only be 1 short left
- *((uint16 *)dst32) = format.swapRedBlue16(*((uint16 *)src32));
- }
-}
-
-
// More challenging -- need to shift
// Assume dst is aligned
void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, uint32 alignSrc) {
PSP_DEBUG_PRINT("copy32Misaligned: dst32[%p], src[%p], bytes[%d], alignSrc[%d]\n", dst32, src, bytes, alignSrc);
-
+
uint32 *src32 = (uint32 *)(((uint32)src) & 0xFFFFFFFC); // remove misalignment
uint32 shiftValue, lastShiftValue;
-
+
switch (alignSrc) {
case 1:
shiftValue = 8;
@@ -320,9 +178,9 @@ void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, u
// Try to do groups of 4 words
uint32 words4 = bytes >> 4;
-
+
srcWord = *src32; // preload 1st word so we read ahead
-
+
for (; words4; words4--) {
dstWord = srcWord >> shiftValue;
srcWord = src32[1];
@@ -343,12 +201,12 @@ void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, u
src32 += 4;
dst32 += 4;
}
-
+
uint32 words = (bytes & 0xF) >> 2; // now get remaining words
-
+
// we read one word ahead of what we write
// setup the first read
-
+
for (; words ;words--) {
dstWord = srcWord >> shiftValue;
srcWord = src32[1]; // we still go one ahead
@@ -356,9 +214,9 @@ void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, u
dstWord |= srcWord << lastShiftValue;
*dst32++ = dstWord;
}
-
+
uint32 bytesLeft = bytes & 3; // and remaining bytes
-
+
if (bytesLeft) {
byte *dst8 = (byte *)dst32;
byte *src8 = ((byte *)src32) + ((uint32)src & 0x3); // get exact location we should be at
@@ -369,14 +227,137 @@ void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, u
}
}
+void PspMemory::testCopy(const byte *debugDst, const byte *debugSrc, uint32 debugBytes) {
+
+ bool mismatch = false;
+ PSP_INFO_PRINT("testing fastCopy...");
+
+ for (uint32 i = 0; i < debugBytes; i++) {
+ if (debugDst[i] != debugSrc[i]) {
+ if (!mismatch) {
+ PSP_INFO_PRINT("**** mismatch in copy! ****\n");
+ PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
+ mismatch = true;
+ }
+ PSP_INFO_PRINT("[%d]%x!=%x ", i, debugSrc[i], debugDst[i]);
+ }
+ }
+ if (mismatch) {
+ PSP_INFO_PRINT("\n");
+ } else {
+ PSP_INFO_PRINT("ok\n");
+ }
+}
+
+//
+// used to swap red and blue
+void PspMemorySwap::swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
+ DEBUG_ENTER_FUNC();
+
+#ifdef TEST_MEMORY_COPY
+ uint32 debugBytes = bytes;
+ const uint16 *debugDst = dst16, *debugSrc = src16;
+#endif
+
+ // align the destination pointer first
+ uint32 prefixDst = (((uint32)dst16) & 0x3); // for swap, we can only have 2 or 0 as our prefix
+
+ if (prefixDst) {
+ bytes -= prefixDst; // remember we assume bytes > 4
+ *dst16++ = format.swapRedBlue16(*src16++);
+
+ if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) { // check if it's worthwhile to continue
+ swap16(dst16, src16, bytes, format);
+
+#ifdef TEST_MEMORY_COPY
+ testSwap(debugDst, debugSrc, debugBytes, format);
+#endif
+ return;
+ }
+ }
+
+ // check the source pointer alignment now
+ uint32 alignSrc = (((uint32)src16) & 0x3);
+
+ if (alignSrc) { // we'll need to realign our reads
+ PSP_DEBUG_PRINT("misaligned copy of %u bytes from %p to %p\n", bytes, src16, dst16);
+ swap32Misaligned((uint32 *)dst16, src16, bytes, format);
+ } else {
+ swap32Aligned((uint32 *)dst16, (const uint32 *)src16, bytes, format);
+ }
+
+#ifdef TEST_MEMORY_COPY
+ testSwap(debugDst, debugSrc, debugBytes, format);
+#endif
+
+}
+
+void PspMemorySwap::testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format) {
+
+ bool mismatch = false;
+ PSP_INFO_PRINT("testing fastSwap...");
+
+ uint32 shorts = debugBytes >> 1;
+
+ for (uint32 i = 0; i < shorts; i++) {
+ if (debugDst[i] != format.swapRedBlue16(debugSrc[i])) {
+ if (!mismatch) {
+ PSP_INFO_PRINT("**** mismatch in swap! ****\n");
+ PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
+ mismatch = true;
+ }
+ PSP_INFO_PRINT("[%d]%x!=%x ", i<<1, format.swapRedBlue16(debugSrc[i]), debugDst[i]);
+ }
+ }
+ if (mismatch) {
+ PSP_INFO_PRINT("\n");
+ } else {
+ PSP_INFO_PRINT("ok\n");
+ }
+}
+
+void PspMemorySwap::swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format) {
+ DEBUG_ENTER_FUNC();
+ int words4 = bytes >> 4;
+
+ // try blocks of 4 words at a time
+ while (words4--) {
+ uint32 a, b, c, d;
+ a = format.swapRedBlue32(src32[0]);
+ b = format.swapRedBlue32(src32[1]);
+ c = format.swapRedBlue32(src32[2]);
+ d = format.swapRedBlue32(src32[3]);
+ dst32[0] = a;
+ dst32[1] = b;
+ dst32[2] = c;
+ dst32[3] = d;
+ dst32 += 4;
+ src32 += 4;
+ }
+
+ uint32 bytesLeft = bytes & 0xF;
+ uint32 words = bytesLeft >> 2;
+
+ // now just do words
+ while (words--) {
+ *dst32++ = format.swapRedBlue32(*src32++);
+ }
+
+ bytesLeft = bytes & 0x3;
+
+ if (bytesLeft) { // for swap, can only be 1 short left
+ *((uint16 *)dst32) = format.swapRedBlue16(*((uint16 *)src32));
+ }
+}
+
// More challenging -- need to shift
// We assume dst is aligned
-void PspMemory::swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
+void PspMemorySwap::swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
DEBUG_ENTER_FUNC();
const uint32 shiftValue = 16;
uint32 *src32 = (uint32 *)(((uint32)src16) & 0xFFFFFFFC); // remove misalignment
-
+
// Try to do groups of 4 words
uint32 words4 = bytes >> 4;
uint32 srcWord = src32[0]; // preload
@@ -401,15 +382,15 @@ void PspMemory::swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 byte
src32 += 4;
dst32 += 4;
}
-
+
uint32 words = (bytes & 0xF) >> 2;
-
+
// we read one word ahead of what we write
// setup the first read
if (words) {
//srcWord = *src32++; // don't need this. already loaded
src32++; // we already have the value loaded in
-
+
while (words--) {
uint32 dstWord = srcWord >> shiftValue;
srcWord = *src32++;
@@ -417,86 +398,10 @@ void PspMemory::swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 byte
*dst32++ = format.swapRedBlue32(dstWord);
}
}
-
+
uint32 bytesLeft = bytes & 3;
-
+
if (bytesLeft) { // for swap, can only be 1 short left
*((uint16 *)dst32) = format.swapRedBlue16((uint16)(srcWord >> shiftValue));
}
}
-
-inline void PspMemory::copy16(uint16 *dst16, const uint16 *src16, uint32 bytes) {
- PSP_DEBUG_PRINT("copy16(): dst16[%p], src16[%p], bytes[%d]\n", dst16, src16, bytes);
-
- uint32 shorts = bytes >> 1;
- uint32 remainingBytes = bytes & 1;
-
- for (; shorts > 0 ; shorts--) {
- *dst16++ = *src16++;
- }
- if (remainingBytes)
- *(byte *)dst16 = *(byte *)src16;
-}
-
-// Class VramAllocator -----------------------------------
-
-DECLARE_SINGLETON(VramAllocator)
-
-//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
-//#define __PSP_DEBUG_PRINT__
-
-#include "backends/platform/psp/trace.h"
-
-
-void *VramAllocator::allocate(int32 size, bool smallAllocation /* = false */) {
- DEBUG_ENTER_FUNC();
- assert(size > 0);
-
- byte *lastAddress = smallAllocation ? (byte *)VRAM_SMALL_ADDRESS : (byte *)VRAM_START_ADDRESS;
- Common::List<Allocation>::iterator i;
-
- // Find a block that fits, starting from the beginning
- for (i = _allocList.begin(); i != _allocList.end(); ++i) {
- byte *currAddress = (*i).address;
-
- if (currAddress - lastAddress >= size) // We found a match
- break;
-
- if ((*i).getEnd() > lastAddress)
- lastAddress = (byte *)(*i).getEnd();
- }
-
- if (lastAddress + size > (byte *)VRAM_END_ADDRESS) {
- PSP_DEBUG_PRINT("No space for allocation of %d bytes. %d bytes already allocated.\n",
- size, _bytesAllocated);
- return NULL;
- }
-
- _allocList.insert(i, Allocation(lastAddress, size));
- _bytesAllocated += size;
-
- PSP_DEBUG_PRINT("Allocated in VRAM, size %u at %p.\n", size, lastAddress);
- PSP_DEBUG_PRINT("Total allocated %u, remaining %u.\n", _bytesAllocated, (2 * 1024 * 1024) - _bytesAllocated);
-
- return lastAddress;
-}
-
-// Deallocate a block from VRAM
-void VramAllocator::deallocate(void *address) {
- DEBUG_ENTER_FUNC();
- address = (byte *)CACHED(address); // Make sure all addresses are the same
-
- Common::List<Allocation>::iterator i;
-
- // Find the Allocator to deallocate
- for (i = _allocList.begin(); i != _allocList.end(); ++i) {
- if ((*i).address == address) {
- _bytesAllocated -= (*i).size;
- _allocList.erase(i);
- PSP_DEBUG_PRINT("Deallocated address[%p], size[%u]\n", (*i).address, (*i).size);
- return;
- }
- }
-
- PSP_DEBUG_PRINT("Address[%p] not allocated.\n", address);
-}
diff --git a/backends/platform/psp/memory.h b/backends/platform/psp/memory.h
index 793bc94888..54e9225b2e 100644
--- a/backends/platform/psp/memory.h
+++ b/backends/platform/psp/memory.h
@@ -27,18 +27,24 @@
#ifndef PSP_MEMORY_H
#define PSP_MEMORY_H
-#include "backends/platform/psp/psppixelformat.h"
-#include "common/list.h"
-
-#define UNCACHED(x) ((byte *)(((uint32)(x)) | 0x40000000)) /* make an uncached access */
-#define CACHED(x) ((byte *)(((uint32)(x)) & 0xBFFFFFFF)) /* make an uncached access into a cached one */
-
#define MIN_AMOUNT_FOR_COMPLEX_COPY 8
#define MIN_AMOUNT_FOR_MISALIGNED_COPY 8
//#define __PSP_DEBUG_PRINT__
-#include "backends/platform/psp/trace.h"
+//#include "backends/platform/psp/trace.h"
+
+// These instructions don't generate automatically but are faster then copying byte by byte
+inline void lwl_copy(byte *dst, const byte *src) {
+ register uint32 data;
+ asm volatile ("lwr %0,0(%1)\n\t"
+ "lwl %0,3(%1)\n\t"
+ : "=&r" (data) : "r" (src), "m" (*src));
+
+ asm volatile ("swr %1,0(%2)\n\t"
+ "swl %1,3(%2)\n\t"
+ : "=m" (*dst) : "r" (data), "r" (dst));
+}
/**
* Class that does memory copying and swapping if needed
@@ -46,42 +52,69 @@
class PspMemory {
private:
static void testCopy(const byte *debugDst, const byte *debugSrc, uint32 debugBytes);
- static void testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format);
static void copy(byte *dst, const byte *src, uint32 bytes);
- static void swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
static void copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes);
- static void swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format);
static void copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, uint32 alignSrc);
- static void swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
- static void copy16(uint16 *dst, const uint16 *src, uint32 bytes);
-
- // For swapping, we know that we have multiples of 16 bits
- static void swap16(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
- PSP_DEBUG_PRINT("swap16 called with dst16[%p], src16[%p], bytes[%d]\n", dst16, src16, bytes);
- uint32 shorts = bytes >> 1;
- while (shorts--) {
- *dst16++ = format.swapRedBlue16(*src16++);
+ static inline void copy8(byte *dst, const byte *src, int32 bytes) {
+ //PSP_DEBUG_PRINT("copy8 called with dst[%p], src[%p], bytes[%d]\n", dst, src, bytes);
+ uint32 words = bytes >> 2;
+ for (; words; words--) {
+ lwl_copy(dst, src);
+ dst += 4;
+ src += 4;
}
- }
-
- static void copy8(byte *dst, const byte *src, uint32 bytes) {
- PSP_DEBUG_PRINT("copy8 called with dst[%p], src[%p], bytes[%d]\n", dst, src, bytes);
- while (bytes--) {
+
+ uint32 bytesLeft = bytes & 0x3;
+ for (; bytesLeft; bytesLeft--) {
*dst++ = *src++;
}
}
-public:
+public:
// This is the interface to the outside world
- static void fastCopy(byte *dst, const byte *src, uint32 bytes) {
+ static void *fastCopy(void *dstv, const void *srcv, int32 bytes) {
+ byte *dst = (byte *)dstv;
+ byte *src = (byte *)srcv;
+
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) {
copy8(dst, src, bytes);
} else { // go to more powerful copy
copy(dst, src, bytes);
}
+
+ return dstv;
}
+};
+
+inline void *psp_memcpy(void *dst, const void *src, int32 bytes) {
+ return PspMemory::fastCopy(dst, src, bytes);
+}
+
+#endif /* PSP_MEMORY_H */
+
+#if defined(PSP_INCLUDE_SWAP) && !defined(PSP_MEMORY_SWAP_H)
+#define PSP_MEMORY_SWAP_H
+//#include "backends/platform/psp/psppixelformat.h"
+
+class PspMemorySwap {
+private:
+ static void testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format);
+ static void swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
+ static void swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format);
+ static void swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
+ // For swapping, we know that we have multiples of 16 bits
+ static void swap16(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
+ PSP_DEBUG_PRINT("swap16 called with dst16[%p], src16[%p], bytes[%d]\n", dst16, src16, bytes);
+ uint32 shorts = bytes >> 1;
+
+ while (shorts--) {
+ *dst16++ = format.swapRedBlue16(*src16++);
+ }
+}
+
+public:
static void fastSwap(byte *dst, const byte *src, uint32 bytes, PSPPixelFormat &format) {
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY * 2) {
swap16((uint16 *)dst, (uint16 *)src, bytes, format);
@@ -91,41 +124,6 @@ public:
}
};
-/**
- * Class that allocates memory in the VRAM
- */
-class VramAllocator : public Common::Singleton<VramAllocator> {
-public:
- VramAllocator() : _bytesAllocated(0) {}
- void *allocate(int32 size, bool smallAllocation = false); // smallAllocation e.g. palettes
- void deallocate(void *pointer);
-
- static inline bool isAddressInVram(void *address) {
- if ((uint32)(CACHED(address)) >= VRAM_START_ADDRESS && (uint32)(CACHED(address)) < VRAM_END_ADDRESS)
- return true;
- return false;
- }
-
+#endif /* PSP_INCLUDE_SWAP */
-private:
- /**
- * Used to allocate in VRAM
- */
- struct Allocation {
- byte *address;
- uint32 size;
- void *getEnd() { return address + size; }
- Allocation(void *Address, uint32 Size) : address((byte *)Address), size(Size) {}
- Allocation() : address(0), size(0) {}
- };
-
- enum {
- VRAM_START_ADDRESS = 0x04000000,
- VRAM_END_ADDRESS = 0x04200000,
- VRAM_SMALL_ADDRESS = VRAM_END_ADDRESS - (4 * 1024) // 4K in the end for small allocations
- };
- Common::List <Allocation> _allocList; // List of allocations
- uint32 _bytesAllocated;
-};
-#endif /* PSP_MEMORY_H */
diff --git a/backends/platform/psp/module.mk b/backends/platform/psp/module.mk
index a854ec1252..e3eac153dd 100644
--- a/backends/platform/psp/module.mk
+++ b/backends/platform/psp/module.mk
@@ -16,7 +16,10 @@ MODULE_OBJS := powerman.o \
thread.o \
rtc.o \
mp3.o \
- tests.o
+ png_loader.o \
+ image_viewer.o \
+ tests.o \
+ dummy.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
diff --git a/backends/platform/psp/mp3.cpp b/backends/platform/psp/mp3.cpp
index 98e8a0404b..0e88418f13 100644
--- a/backends/platform/psp/mp3.cpp
+++ b/backends/platform/psp/mp3.cpp
@@ -38,13 +38,13 @@
#include <pspsysmem.h>
#include <pspmodulemgr.h>
#include <psputility_avmodules.h>
-#include <mad.h>
+#include <mad.h>
#include "backends/platform/psp/mp3.h"
//#define DISABLE_PSP_MP3 // to make us use the regular MAD decoder instead
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
-//#define __PSP_DEBUG_PRINT__
+//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
//#define PRINT_BUFFERS /* to debug MP3 buffers */
@@ -77,7 +77,7 @@ enum {
bool Mp3PspStream::initDecoder() {
DEBUG_ENTER_FUNC();
-
+
if (_decoderInit) {
PSP_ERROR("Already initialized!");
return true;
@@ -97,15 +97,15 @@ bool Mp3PspStream::initDecoder() {
PSP_ERROR("failed to load audiocodec.prx. ME cannot start.\n");
_decoderFail = true;
return false;
- }
- } else {
+ }
+ } else {
if (sceUtilityLoadAvModule(PSP_AV_MODULE_AVCODEC) < 0) {
PSP_ERROR("failed to load AVCODEC module. ME cannot start.\n");
_decoderFail = true;
return false;
}
}
-
+
PSP_DEBUG_PRINT("Using PSP's ME for MP3\n"); // important to know this is happening
_decoderInit = true;
@@ -114,10 +114,10 @@ bool Mp3PspStream::initDecoder() {
bool Mp3PspStream::stopDecoder() {
DEBUG_ENTER_FUNC();
-
+
if (!_decoderInit)
return true;
-
+
// Based on PSP firmware version, we need to do different things to do Media Engine processing
if (sceKernelDevkitVersion() == 0x01050001){ // TODO: how do we unload?
/* if (!unloadAudioModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL) ||
@@ -130,10 +130,10 @@ bool Mp3PspStream::stopDecoder() {
if (sceUtilityUnloadModule(PSP_MODULE_AV_AVCODEC) < 0) {
PSP_ERROR("failed to unload avcodec module\n");
return false;
- }
+ }
}
-
- _decoderInit = false;
+
+ _decoderInit = false;
return true;
}
@@ -176,26 +176,27 @@ Mp3PspStream::Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse
_length(0, 1000),
_sampleRate(0),
_totalTime(mad_timer_zero) {
-
+
DEBUG_ENTER_FUNC();
assert(_decoderInit); // must be initialized by now
-
+
// let's leave the buffer guard -- who knows, it may be good?
memset(_buf, 0, sizeof(_buf));
memset(_codecInBuffer, 0, sizeof(_codecInBuffer));
-
+
initStream(); // init needed stuff for the stream
findValidHeader(); // get a first header so we can read basic stuff
-
+
_sampleRate = _header.samplerate; // copy it before it gets destroyed
-
+ _stereo = (MAD_NCHANNELS(&_header) == 2);
+
while (_state != MP3_STATE_EOS)
findValidHeader(); // get a first header so we can read basic stuff
-
+
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
-
+
deinitStream();
_state = MP3_STATE_INIT;
@@ -221,7 +222,7 @@ int Mp3PspStream::initStream() {
// Read the first few sample bytes into the buffer
readMP3DataIntoBuffer();
-
+
return true;
}
@@ -229,7 +230,7 @@ bool Mp3PspStream::initStreamME() {
// The following will eventually go into the thread
memset(_codecParams, 0, sizeof(_codecParams));
-
+
// Init the MP3 hardware
int ret = 0;
ret = sceAudiocodecCheckNeedMem(_codecParams, 0x1002);
@@ -244,22 +245,22 @@ bool Mp3PspStream::initStreamME() {
return false;
}
PSP_DEBUG_PRINT("sceAudioCodecGetEDRAM returned %d\n", ret);
-
+
PSP_DEBUG_PRINT("samplerate[%d]\n", _sampleRate);
_codecParams[10] = _sampleRate;
-
+
ret = sceAudiocodecInit(_codecParams, 0x1002);
if (ret < 0) {
PSP_ERROR("failed to init MP3 ME module. sceAudiocodecInit returned 0x%x.\n", ret);
return false;
}
-
+
return true;
}
Mp3PspStream::~Mp3PspStream() {
DEBUG_ENTER_FUNC();
-
+
deinitStream();
releaseStreamME(); // free the memory used for this stream
@@ -276,17 +277,17 @@ void Mp3PspStream::deinitStream() {
// Deinit MAD
mad_header_finish(&_header);
mad_stream_finish(&_stream);
-
+
_state = MP3_STATE_EOS;
}
void Mp3PspStream::releaseStreamME() {
sceAudiocodecReleaseEDRAM(_codecParams);
-}
+}
void Mp3PspStream::decodeMP3Data() {
DEBUG_ENTER_FUNC();
-
+
do {
if (_state == MP3_STATE_INIT) {
initStream();
@@ -295,17 +296,17 @@ void Mp3PspStream::decodeMP3Data() {
if (_state == MP3_STATE_EOS)
return;
-
+
findValidHeader(); // seach for next valid header
while (_state == MP3_STATE_READY) { // not a real 'while'. Just for easy flow
_stream.error = MAD_ERROR_NONE;
uint32 frame_size = _stream.next_frame - _stream.this_frame;
-
- updatePcmLength(); // Retrieve the number of PCM samples.
+
+ updatePcmLength(); // Retrieve the number of PCM samples.
// We seem to change this, so it needs to be dynamic
-
+
PSP_DEBUG_PRINT("MP3 frame size[%d]. pcmLength[%d]\n", frame_size, _pcmLength);
memcpy(_codecInBuffer, _stream.this_frame, frame_size); // we need it aligned
@@ -315,7 +316,7 @@ void Mp3PspStream::decodeMP3Data() {
_codecParams[8] = (unsigned long)_pcmSamples;
_codecParams[7] = frame_size;
_codecParams[9] = _pcmLength * 2; // x2 for stereo, though this one's not so important
-
+
// debug
#ifdef PRINT_BUFFERS
PSP_DEBUG_PRINT("mp3 frame:\n");
@@ -415,10 +416,13 @@ bool Mp3PspStream::seek(const Timestamp &where) {
mad_timer_t destination;
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
+ // Important to release and re-init the ME
+ releaseStreamME();
+ initStreamME();
+
// Check if we need to rewind
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) {
initStream();
- initStreamME();
}
// Skip ahead
@@ -469,38 +473,38 @@ int Mp3PspStream::readBuffer(int16 *buffer, const int numSamples) {
DEBUG_ENTER_FUNC();
int samples = 0;
-#ifdef PRINT_BUFFERS
+#ifdef PRINT_BUFFERS
int16 *debugBuffer = buffer;
-#endif
-
+#endif
+
// Keep going as long as we have input available
while (samples < numSamples && _state != MP3_STATE_EOS) {
const int len = MIN(numSamples, samples + (int)(_pcmLength - _posInFrame) * MAD_NCHANNELS(&_header));
-
+
while (samples < len) {
*buffer++ = _pcmSamples[_posInFrame << 1];
samples++;
if (MAD_NCHANNELS(&_header) == 2) {
*buffer++ = _pcmSamples[(_posInFrame << 1) + 1];
samples++;
- }
+ }
_posInFrame++; // always skip an extra sample since ME always outputs stereo
}
-
+
if (_posInFrame >= _pcmLength) {
// We used up all PCM data in the current frame -- read & decode more
decodeMP3Data();
}
}
-
+
#ifdef PRINT_BUFFERS
PSP_INFO_PRINT("buffer:\n");
for (int i = 0; i<numSamples; i++)
PSP_INFO_PRINT("%d ", debugBuffer[i]);
PSP_INFO_PRINT("\n\n");
-#endif
-
+#endif
+
return samples;
}
-} // End of namespace Audio \ No newline at end of file
+} // End of namespace Audio
diff --git a/backends/platform/psp/mp3.h b/backends/platform/psp/mp3.h
index 983f4cb7f4..1d2fe5ec2f 100644
--- a/backends/platform/psp/mp3.h
+++ b/backends/platform/psp/mp3.h
@@ -53,29 +53,30 @@ protected:
Common::SeekableReadStream *_inStream;
DisposeAfterUse::Flag _disposeAfterUse;
-
+
uint32 _pcmLength; // how many pcm samples we have for this type of file (x2 this for stereo)
-
+
uint _posInFrame; // position in frame
State _state; // what state the stream is in
Timestamp _length;
uint32 _sampleRate;
+ bool _stereo;
mad_timer_t _totalTime;
mad_stream _stream; //
mad_header _header; // This is all we need from libmad
-
+
static bool _decoderInit; // has the decoder been initialized
static bool _decoderFail; // has the decoder failed to load
-
+
enum {
BUFFER_SIZE = 5 * 8192
};
// This buffer contains a slab of input data
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
-
+
void decodeMP3Data();
void readMP3DataIntoBuffer();
@@ -84,19 +85,19 @@ protected:
void findValidHeader();
void deinitStream();
void updatePcmLength();
-
+
// to init and uninit ME decoder
static bool initDecoder();
static bool stopDecoder();
-
+
// ME functions for stream
bool initStreamME();
void releaseStreamME();
-
+
public:
Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
~Mp3PspStream();
-
+
// This function avoids having to create streams when it's not possible
static inline bool isOkToCreateStream() {
if (_decoderFail) // fatal failure
@@ -104,13 +105,13 @@ public:
if (!_decoderInit) // if we're not initialized
if (!initDecoder()) // check if we failed init
return false;
- return true;
+ return true;
}
int readBuffer(int16 *buffer, const int numSamples);
bool endOfData() const { return _state == MP3_STATE_EOS; }
- bool isStereo() const { return MAD_NCHANNELS(&_header) == 2; }
+ bool isStereo() const { return _stereo; }
int getRate() const { return _sampleRate; }
bool seek(const Timestamp &where);
diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp
index b09d9c0c00..40c074ae00 100644
--- a/backends/platform/psp/osys_psp.cpp
+++ b/backends/platform/psp/osys_psp.cpp
@@ -67,7 +67,7 @@ void OSystem_PSP::initBackend() {
// Instantiate real time clock
PspRtc::instance();
-
+
_cursor.enableCursorPalette(false);
_cursor.setXY(PSP_SCREEN_WIDTH >> 1, PSP_SCREEN_HEIGHT >> 1); // Mouse in the middle of the screen
@@ -76,12 +76,18 @@ void OSystem_PSP::initBackend() {
_displayManager.setScreen(&_screen);
_displayManager.setOverlay(&_overlay);
_displayManager.setKeyboard(&_keyboard);
+ _displayManager.setImageViewer(&_imageViewer);
_displayManager.init();
// Set pointers for input handler
_inputHandler.setCursor(&_cursor);
_inputHandler.setKeyboard(&_keyboard);
+ _inputHandler.setImageViewer(&_imageViewer);
_inputHandler.init();
+
+ // Set pointers for image viewer
+ _imageViewer.setInputHandler(&_inputHandler);
+ _imageViewer.setDisplayManager(&_displayManager);
_savefile = new PSPSaveFileManager;
@@ -97,6 +103,12 @@ void OSystem_PSP::initBackend() {
OSystem::initBackend();
}
+// Let's us know an engine
+void OSystem_PSP::engineDone() {
+ // for now, all we need is to reset the image number on the viewer
+ _imageViewer.resetOnEngineDone();
+}
+
bool OSystem_PSP::hasFeature(Feature f) {
return (f == kFeatureOverlaySupportsAlpha || f == kFeatureCursorHasPalette);
}
@@ -148,7 +160,7 @@ Common::List<Graphics::PixelFormat> OSystem_PSP::getSupportedFormats() const {
void OSystem_PSP::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_displayManager.setSizeAndPixelFormat(width, height, format);
_cursor.setVisible(false);
@@ -167,7 +179,7 @@ int16 OSystem_PSP::getHeight() {
void OSystem_PSP::setPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_screen.setPartialPalette(colors, start, num);
_cursor.setScreenPalette(colors, start, num);
_cursor.clearKeyColor();
@@ -175,7 +187,7 @@ void OSystem_PSP::setPalette(const byte *colors, uint start, uint num) {
void OSystem_PSP::setCursorPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_cursor.setCursorPalette(colors, start, num);
_cursor.enableCursorPalette(true);
_cursor.clearKeyColor(); // Do we need this?
@@ -183,25 +195,25 @@ void OSystem_PSP::setCursorPalette(const byte *colors, uint start, uint num) {
void OSystem_PSP::disableCursorPalette(bool disable) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_cursor.enableCursorPalette(!disable);
}
void OSystem_PSP::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_screen.copyFromRect(buf, pitch, x, y, w, h);
}
Graphics::Surface *OSystem_PSP::lockScreen() {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
return _screen.lockAndGetForEditing();
}
void OSystem_PSP::unlockScreen() {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
// The screen is always completely updated anyway, so we don't have to force a full update here.
_screen.unlock();
}
@@ -219,7 +231,7 @@ void OSystem_PSP::setShakePos(int shakeOffset) {
void OSystem_PSP::showOverlay() {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_overlay.setVisible(true);
_cursor.setLimits(_overlay.getWidth(), _overlay.getHeight());
_cursor.useGlobalScaler(false); // mouse with overlay is 1:1
@@ -227,7 +239,7 @@ void OSystem_PSP::showOverlay() {
void OSystem_PSP::hideOverlay() {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_overlay.setVisible(false);
_cursor.setLimits(_screen.getWidth(), _screen.getHeight());
_cursor.useGlobalScaler(true); // mouse needs to be scaled with screen
@@ -235,7 +247,7 @@ void OSystem_PSP::hideOverlay() {
void OSystem_PSP::clearOverlay() {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_overlay.clearBuffer();
}
@@ -246,7 +258,7 @@ void OSystem_PSP::grabOverlay(OverlayColor *buf, int pitch) {
void OSystem_PSP::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_overlay.copyFromRect(buf, pitch, x, y, w, h);
}
@@ -266,7 +278,7 @@ void OSystem_PSP::grabPalette(byte *colors, uint start, uint num) {
bool OSystem_PSP::showMouse(bool v) {
DEBUG_ENTER_FUNC();
_pendingUpdate = false;
-
+
PSP_DEBUG_PRINT("%s\n", v ? "true" : "false");
bool last = _cursor.isVisible();
_cursor.setVisible(v);
@@ -276,14 +288,14 @@ bool OSystem_PSP::showMouse(bool v) {
void OSystem_PSP::warpMouse(int x, int y) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
+ _pendingUpdate = false;
_cursor.setXY(x, y);
}
void OSystem_PSP::setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
- _pendingUpdate = false;
-
+ _pendingUpdate = false;
+
PSP_DEBUG_PRINT("pbuf[%p], w[%u], h[%u], hotspot:X[%d], Y[%d], keycolor[%d], scale[%d], pformat[%p]\n", buf, w, h, hotspotX, hotspotY, keycolor, cursorTargetScale, format);
if (format) {
PSP_DEBUG_PRINT("format: bpp[%d], rLoss[%d], gLoss[%d], bLoss[%d], aLoss[%d], rShift[%d], gShift[%d], bShift[%d], aShift[%d]\n", format->bytesPerPixel, format->rLoss, format->gLoss, format->bLoss, format->aLoss, format->rShift, format->gShift, format->bShift, format->aShift);
@@ -310,16 +322,16 @@ bool OSystem_PSP::pollEvent(Common::Event &event) {
// Time between event polls is usually 5-10ms, so waiting for 4 calls before checking to update the screen should be fine
if (_pendingUpdate) {
_pendingUpdateCounter++;
-
+
if (_pendingUpdateCounter >= 4) {
PSP_DEBUG_PRINT("servicing pending update\n");
updateScreen();
if (!_pendingUpdate) // we handled the update
- _pendingUpdateCounter = 0;
+ _pendingUpdateCounter = 0;
}
- } else
+ } else
_pendingUpdateCounter = 0; // reset the counter, no pending
-
+
return _inputHandler.getAllInputs(event);
}
diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h
index 5721296c94..52b8f4e887 100644
--- a/backends/platform/psp/osys_psp.h
+++ b/backends/platform/psp/osys_psp.h
@@ -32,10 +32,12 @@
#include "sound/mixer_intern.h"
#include "backends/base-backend.h"
#include "backends/fs/psp/psp-fs-factory.h"
+
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"
#include "backends/platform/psp/pspkeyboard.h"
+#include "backends/platform/psp/image_viewer.h"
#include "backends/platform/psp/display_manager.h"
#include "backends/platform/psp/input.h"
#include "backends/platform/psp/audio.h"
@@ -60,6 +62,7 @@ private:
InputHandler _inputHandler;
PspAudio _audio;
PspTimer _pspTimer;
+ ImageViewer _imageViewer;
public:
OSystem_PSP() : _savefile(0), _mixer(0), _timer(0), _pendingUpdate(false), _pendingUpdateCounter(0) {}
@@ -146,6 +149,7 @@ public:
Common::SaveFileManager *getSavefileManager() { return _savefile; }
FilesystemFactory *getFilesystemFactory() { return &PSPFilesystemFactory::instance(); }
void getTimeAndDate(TimeDate &t) const;
+ virtual void engineDone();
void quit();
diff --git a/backends/platform/psp/png_loader.cpp b/backends/platform/psp/png_loader.cpp
new file mode 100644
index 0000000000..08f370f36d
--- /dev/null
+++ b/backends/platform/psp/png_loader.cpp
@@ -0,0 +1,210 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+#include "backends/platform/psp/psppixelformat.h"
+#include "backends/platform/psp/display_client.h"
+#include "backends/platform/psp/png_loader.h"
+
+//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
+//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
+
+#include "backends/platform/psp/trace.h"
+
+PngLoader::Status PngLoader::allocate() {
+ DEBUG_ENTER_FUNC();
+
+ if (!findImageDimensions()) {
+ PSP_ERROR("failed to get image dimensions\n");
+ return BAD_FILE;
+ }
+
+ _buffer->setSize(_width, _height, _sizeBy);
+
+ uint32 bitsPerPixel = _bitDepth * _channels;
+
+ if (_paletteSize) { // 8 or 4-bit image
+ if (bitsPerPixel == 4) {
+ _buffer->setPixelFormat(PSPPixelFormat::Type_Palette_4bit);
+ _palette->setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_4bit);
+ _paletteSize = 16; // round up
+ } else if (bitsPerPixel == 8) { // 8-bit image
+ _buffer->setPixelFormat(PSPPixelFormat::Type_Palette_8bit);
+ _palette->setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_8bit);
+ _paletteSize = 256; // round up
+ } else {
+ PSP_ERROR("too many bits per pixel[%d] for a palette\n", bitsPerPixel);
+ return BAD_FILE;
+ }
+
+ } else { // 32-bit image
+ _buffer->setPixelFormat(PSPPixelFormat::Type_8888);
+ }
+
+ if (!_buffer->allocate()) {
+ PSP_ERROR("failed to allocate buffer\n");
+ return OUT_OF_MEMORY;
+ }
+ if (_buffer->hasPalette() && !_palette->allocate()) {
+ PSP_ERROR("failed to allocate palette\n");
+ return OUT_OF_MEMORY;
+ }
+ return OK;
+}
+
+bool PngLoader::load() {
+ DEBUG_ENTER_FUNC();
+ // Try to load the image
+ _file->seek(0); // Go back to start
+
+ if (!loadImageIntoBuffer()) {
+ PSP_DEBUG_PRINT("failed to load image\n");
+ return false;
+ }
+
+ PSP_DEBUG_PRINT("succeded in loading image\n");
+
+ if (_paletteSize == 16) // 4-bit
+ _buffer->flipNibbles(); // required because of PNG 4-bit format
+ return true;
+}
+
+void PngLoader::warningFn(png_structp png_ptr, png_const_charp warning_msg) {
+ // ignore PNG warnings
+}
+
+// Read function for png library to be able to read from our SeekableReadStream
+//
+void PngLoader::libReadFunc(png_structp pngPtr, png_bytep data, png_size_t length) {
+ Common::SeekableReadStream *file;
+
+ file = (Common::SeekableReadStream *)pngPtr->io_ptr;
+
+ file->read(data, length);
+}
+
+bool PngLoader::basicImageLoad() {
+ DEBUG_ENTER_FUNC();
+ _pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!_pngPtr)
+ return false;
+
+ png_set_error_fn(_pngPtr, (png_voidp) NULL, (png_error_ptr) NULL, warningFn);
+
+ _infoPtr = png_create_info_struct(_pngPtr);
+ if (!_infoPtr) {
+ png_destroy_read_struct(&_pngPtr, png_infopp_NULL, png_infopp_NULL);
+ return false;
+ }
+ // Set the png lib to use our read function
+ png_set_read_fn(_pngPtr, (void *)_file, libReadFunc);
+
+ unsigned int sig_read = 0;
+
+ png_set_sig_bytes(_pngPtr, sig_read);
+ png_read_info(_pngPtr, _infoPtr);
+ int interlaceType;
+ png_get_IHDR(_pngPtr, _infoPtr, (png_uint_32 *)&_width, (png_uint_32 *)&_height, &_bitDepth,
+ &_colorType, &interlaceType, int_p_NULL, int_p_NULL);
+ _channels = png_get_channels(_pngPtr, _infoPtr);
+
+ if (_colorType & PNG_COLOR_MASK_PALETTE)
+ _paletteSize = _infoPtr->num_palette;
+
+ return true;
+}
+
+/* Get the width and height of a png image */
+bool PngLoader::findImageDimensions() {
+ DEBUG_ENTER_FUNC();
+
+ bool status = basicImageLoad();
+
+ PSP_DEBUG_PRINT("width[%d], height[%d], paletteSize[%d], bitDepth[%d], channels[%d], rowBytes[%d]\n", _width, _height, _paletteSize, _bitDepth, _channels, _infoPtr->rowbytes);
+ png_destroy_read_struct(&_pngPtr, &_infoPtr, png_infopp_NULL);
+ return status;
+}
+
+//
+// Load a texture from a png image
+//
+bool PngLoader::loadImageIntoBuffer() {
+ DEBUG_ENTER_FUNC();
+
+ if (!basicImageLoad()) {
+ png_destroy_read_struct(&_pngPtr, &_infoPtr, png_infopp_NULL);
+ return false;
+ }
+ png_set_strip_16(_pngPtr); // Strip off 16 bit channels in case they occur
+
+ if (_paletteSize) {
+ // Copy the palette
+ png_colorp srcPal = _infoPtr->palette;
+ for (int i = 0; i < _infoPtr->num_palette; i++) {
+ unsigned char alphaVal = (i < _infoPtr->num_trans) ? _infoPtr->trans[i] : 0xFF; // Load alpha if it's there
+ _palette->setSingleColorRGBA(i, srcPal->red, srcPal->green, srcPal->blue, alphaVal);
+ srcPal++;
+ }
+ } else { // Not a palettized image
+ if (_colorType == PNG_COLOR_TYPE_GRAY && _bitDepth < 8)
+ png_set_gray_1_2_4_to_8(_pngPtr); // Round up grayscale images
+ if (png_get_valid(_pngPtr, _infoPtr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(_pngPtr); // Convert trans channel to alpha for 32 bits
+
+ png_set_add_alpha(_pngPtr, 0xff, PNG_FILLER_AFTER); // Filler for alpha if none exists
+ }
+
+ uint32 rowBytes = png_get_rowbytes(_pngPtr, _infoPtr);
+
+ // there seems to be a bug in libpng where it doesn't increase the rowbytes or the
+ // channel even after we add the alpha channel
+ if (_channels == 3 && (rowBytes / _width) == 3) {
+ _channels = 4;
+ rowBytes = _width * _channels;
+ }
+
+ PSP_DEBUG_PRINT("rowBytes[%d], channels[%d]\n", rowBytes, _channels);
+
+ unsigned char *line = (unsigned char*) malloc(rowBytes);
+ if (!line) {
+ png_destroy_read_struct(&_pngPtr, png_infopp_NULL, png_infopp_NULL);
+ PSP_ERROR("Couldn't allocate line\n");
+ return false;
+ }
+
+ for (size_t y = 0; y < _height; y++) {
+ png_read_row(_pngPtr, line, png_bytep_NULL);
+ _buffer->copyFromRect(line, rowBytes, 0, y, _width, 1); // Copy into buffer
+ }
+ free(line);
+ png_read_end(_pngPtr, _infoPtr);
+ png_destroy_read_struct(&_pngPtr, &_infoPtr, png_infopp_NULL);
+
+ return true;
+}
diff --git a/backends/platform/psp/png_loader.h b/backends/platform/psp/png_loader.h
new file mode 100644
index 0000000000..4119bfef2b
--- /dev/null
+++ b/backends/platform/psp/png_loader.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 PSP_PNG_IMAGE_H
+#define PSP_PNG_IMAGE_H
+
+#include <png.h>
+
+class PngLoader {
+private:
+ bool basicImageLoad(); // common operation
+ bool findImageDimensions(); // find dimensions of a given PNG file
+ bool loadImageIntoBuffer();
+
+ static void warningFn(png_structp png_ptr, png_const_charp warning_msg);
+ static void libReadFunc(png_structp pngPtr, png_bytep data, png_size_t length);
+
+ Common::SeekableReadStream *_file;
+ Buffer *_buffer;
+ Palette *_palette;
+
+ uint32 _width;
+ uint32 _height;
+ uint32 _paletteSize;
+ Buffer::HowToSize _sizeBy;
+
+ // PNG lib values
+ int _bitDepth;
+ png_structp _pngPtr;
+ png_infop _infoPtr;
+ int _colorType;
+ uint32 _channels;
+
+public:
+ enum Status {
+ OK,
+ OUT_OF_MEMORY,
+ BAD_FILE
+ };
+
+ PngLoader(Common::SeekableReadStream *file, Buffer &buffer, Palette &palette,
+ Buffer::HowToSize sizeBy = Buffer::kSizeByTextureSize) :
+ _file(file), _buffer(&buffer), _palette(&palette),
+ _width(0), _height(0), _paletteSize(0),
+ _bitDepth(0), _sizeBy(sizeBy), _pngPtr(0),
+ _infoPtr(0), _colorType(0), _channels(0) {}
+
+ PngLoader::Status allocate();
+ bool load();
+};
+
+#endif /* PSP_PNG_IMAGE_H */
diff --git a/backends/platform/psp/powerman.cpp b/backends/platform/psp/powerman.cpp
index eaadad16c5..869d9b3023 100644
--- a/backends/platform/psp/powerman.cpp
+++ b/backends/platform/psp/powerman.cpp
@@ -84,7 +84,7 @@ bool PowerManager::unregisterForSuspend(Suspendable *item) {
// Unregister from stream list
_listMutex.lock();
-
+
_suspendList.remove(item);
_listCounter--;
@@ -114,11 +114,11 @@ PowerManager::~PowerManager() {
********************************************/
void PowerManager::pollPauseEngine() {
DEBUG_ENTER_FUNC();
-
-
+
+
bool pause = _pauseFlag; // We copy so as not to have multiple values
- if (pause != _pauseFlagOld) {
+ if (pause != _pauseFlagOld) {
if (g_engine) { // Check to see if we have an engine
if (pause && _pauseClientState == UNPAUSED) {
_pauseClientState = PAUSING; // Tell PM we're in the middle of pausing
@@ -147,7 +147,7 @@ bool PowerManager::beginCriticalSection() {
DEBUG_ENTER_FUNC();
bool ret = false;
-
+
_flagMutex.lock();
// Check the access flag
@@ -156,7 +156,7 @@ bool PowerManager::beginCriticalSection() {
PSP_DEBUG_PRINT("I got blocked. ThreadId[%x]\n", sceKernelGetThreadId());
debugPM();
-
+
_threadSleep.wait(_flagMutex);
PSP_DEBUG_PRINT_FUNC("I got released. ThreadId[%x]\n", sceKernelGetThreadId());
@@ -184,11 +184,11 @@ void PowerManager::endCriticalSection() {
if (_suspendFlag) { // If the PM is sleeping, this flag must be set
PSP_DEBUG_PRINT_FUNC("PM is asleep. Waking it up.\n");
debugPM();
-
+
_pmSleep.releaseAll();
-
+
PSP_DEBUG_PRINT_FUNC("Woke up the PM\n");
-
+
debugPM();
}
@@ -198,7 +198,7 @@ void PowerManager::endCriticalSection() {
}
}
- _flagMutex.unlock();
+ _flagMutex.unlock();
}
/*******************************************
@@ -209,7 +209,7 @@ void PowerManager::endCriticalSection() {
void PowerManager::suspend() {
DEBUG_ENTER_FUNC();
- if (_pauseFlag)
+ if (_pauseFlag)
return; // Very important - make sure we only suspend once
scePowerLock(0); // Also critical to make sure PSP doesn't suspend before we're done
@@ -232,9 +232,9 @@ void PowerManager::suspend() {
PspThread::delayMicros(50000); // We wait 50 msec at a time
}
- // It's possible that the polling thread missed our pause event, but there's
+ // It's possible that the polling thread missed our pause event, but there's
// nothing we can do about that.
- // We can't know if there's polling going on or not.
+ // We can't know if there's polling going on or not.
// It's usually not a critical thing anyway.
_PMStatus = kGettingFlagMutexSuspend;
@@ -249,12 +249,12 @@ void PowerManager::suspend() {
// Check if anyone is in a critical section. If so, we'll wait for them
if (_criticalCounter > 0) {
_PMStatus = kWaitCritSectionSuspend;
-
+
_pmSleep.wait(_flagMutex);
-
+
_PMStatus = kDoneWaitingCritSectionSuspend;
- }
-
+ }
+
_flagMutex.unlock();
_PMStatus = kGettingListMutexSuspend;
@@ -275,7 +275,7 @@ void PowerManager::suspend() {
_PMStatus = kDoneSuspend;
scePowerUnlock(0); // Allow the PSP to go to sleep now
-
+
_PMStatus = kDonePowerUnlock;
}
@@ -286,22 +286,22 @@ void PowerManager::suspend() {
********************************************/
void PowerManager::resume() {
DEBUG_ENTER_FUNC();
-
+
_PMStatus = kBeginResume;
// Make sure we can't get another suspend
scePowerLock(0);
_PMStatus = kCheckingPauseFlag;
-
- if (!_pauseFlag)
+
+ if (!_pauseFlag)
return; // Make sure we can only resume once
_PMStatus = kGettingListMutexResume;
// First we notify our Suspendables. Loop over list, calling resume()
_listMutex.lock();
-
+
_PMStatus = kIteratingListResume;
// Iterate
@@ -314,12 +314,12 @@ void PowerManager::resume() {
_PMStatus = kDoneIteratingListResume;
_listMutex.unlock();
-
+
_PMStatus = kGettingFlagMutexResume;
// Now we set the suspend flag to false
_flagMutex.lock();
-
+
_PMStatus = kGotFlagMutexResume;
_suspendFlag = false;
@@ -328,11 +328,11 @@ void PowerManager::resume() {
// Signal the threads to wake up
_threadSleep.releaseAll();
-
+
_PMStatus = kDoneSignallingSuspendedThreadsResume;
_flagMutex.unlock();
-
+
_PMStatus = kDoneResume;
_pauseFlag = false; // Signal engine to unpause -- no mutex needed
diff --git a/backends/platform/psp/psp.spec b/backends/platform/psp/psp.spec
index ac325b7fd6..7177413373 100644
--- a/backends/platform/psp/psp.spec
+++ b/backends/platform/psp/psp.spec
@@ -1,3 +1,3 @@
%rename lib old_lib
*lib:
-%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspaudiocodec -lpspkernel
+%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspaudiocodec -lpspkernel -lpspnet_inet
diff --git a/backends/platform/psp/psp_main.cpp b/backends/platform/psp/psp_main.cpp
index 41cf451323..019f6c12d1 100644
--- a/backends/platform/psp/psp_main.cpp
+++ b/backends/platform/psp/psp_main.cpp
@@ -103,7 +103,7 @@ int exit_callback(void) {
#ifdef ENABLE_PROFILING
gprof_cleanup();
-#endif
+#endif
sceKernelExitGame();
return 0;
@@ -170,12 +170,12 @@ int main(void) {
#endif
/* unit/speed tests */
-#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
+#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
PSP_INFO_PRINT("running tests\n");
psp_tests();
sceKernelSleepThread(); // that's it. That's all we're doing
#endif
-
+
int res = scummvm_main(argc, argv);
g_system->quit(); // TODO: Consider removing / replacing this!
diff --git a/backends/platform/psp/pspkeyboard.cpp b/backends/platform/psp/pspkeyboard.cpp
index eb081fc5f4..f210726692 100644
--- a/backends/platform/psp/pspkeyboard.cpp
+++ b/backends/platform/psp/pspkeyboard.cpp
@@ -23,19 +23,23 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
//#define PSP_KB_SHELL /* Need a hack to properly load the keyboard from the PSP shell */
#ifdef PSP_KB_SHELL
-#define PSP_KB_SHELL_PATH "ms0:/psp/game4xx/scummvm-solid/" /* path to kbd.zip */
+#define PSP_KB_SHELL_PATH "ms0:/psp/game5xx/scummvm-solid/" /* path to kbd.zip */
#endif
#include <malloc.h>
#include <pspkernel.h>
-#include <png.h>
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/pspkeyboard.h"
+#include "backends/platform/psp/png_loader.h"
+#include "backends/platform/psp/input.h"
#include "common/keyboard.h"
#include "common/fs.h"
#include "common/unzip.h"
@@ -77,9 +81,9 @@ short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
},
{ //numbers
{ K('1'), K('2'), K('3'), K('4'), K(0), K(0) },
- { C(F5), C(F8), C(F7), C(F6), C(F9), C(F10) },
+ { C(F5), C(F6), C(F7), C(F8), C(F9), C(F10) },
{ K('5'), K('6'), K('7'), K('8'), K(0), K(0) },
- { C(F1), C(F4), C(F3), C(F2), K(0), K(0) },
+ { C(F1), C(F2), C(F3), C(F4), K(0), K(0) },
{ K('\b'), K('0'), K(' '), K('9'), K(0), K(0) }
},
{ //symbols
@@ -91,16 +95,6 @@ short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
}
};
-// Read function for png library to be able to read from our SeekableReadStream
-//
-void pngReadStreamRead(png_structp png_ptr, png_bytep data, png_size_t length) {
- Common::SeekableReadStream *file;
-
- file = (Common::SeekableReadStream *)png_ptr->io_ptr;
-
- file->read(data, length);
-}
-
// Array with file names
const char *PSPKeyboard::_guiStrings[] = {
"keys4.png", "keys_s4.png",
@@ -281,8 +275,6 @@ bool PSPKeyboard::load() {
// Loop through all png images
for (i = 0; i < guiStringsSize; i++) {
- uint32 height = 0, width = 0, paletteSize = 0;
-
PSP_DEBUG_PRINT("Opening %s.\n", _guiStrings[i]);
// Look for the file in the kbd directory
@@ -309,49 +301,18 @@ bool PSPKeyboard::load() {
goto ERROR;
}
- if (getPngImageSize(file, &width, &height, &paletteSize) == 0) { // Check image size and palette size
- // Allocate memory for image
- PSP_DEBUG_PRINT("width[%d], height[%d], paletteSize[%d]\n", width, height, paletteSize);
- _buffers[i].setSize(width, height, Buffer::kSizeByTextureSize);
-
- if (paletteSize) { // 8 or 4-bit image
- if (paletteSize <= 16) { // 4 bit
- _buffers[i].setPixelFormat(PSPPixelFormat::Type_Palette_4bit);
- _palettes[i].setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_4bit);
- paletteSize = 16;
- } else if (paletteSize <= 256) { // 8-bit image
- paletteSize = 256;
- _buffers[i].setPixelFormat(PSPPixelFormat::Type_Palette_8bit);
- _palettes[i].setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_8bit);
- } else {
- PSP_ERROR("palette of %d too big!\n", paletteSize);
- goto ERROR;
- }
-
- } else { // 32-bit image
- _buffers[i].setPixelFormat(PSPPixelFormat::Type_8888);
- }
+ PngLoader image(file, _buffers[i], _palettes[i]);
- _buffers[i].allocate();
- _palettes[i].allocate();
-
- // Try to load the image
- file->seek(0); // Go back to start
-
- if (loadPngImage(file, _buffers[i], _palettes[i]) != 0)
- goto ERROR;
- else { // Success
- PSP_DEBUG_PRINT("Managed to load the image\n");
-
- if (paletteSize == 16) // 4-bit
- _buffers[i].flipNibbles();
-
- delete file;
- }
- } else {
- PSP_ERROR("couldn't obtain PNG image size\n");
+ if (image.allocate() != PngLoader::OK) {
+ PSP_ERROR("Failed to allocate memory for keyboard image %s\n", _guiStrings[i]);
goto ERROR;
}
+ if (!image.load()) {
+ PSP_ERROR("Failed to load image from file %s\n", _guiStrings[i]);
+ goto ERROR;
+ }
+
+ delete file;
} /* for loop */
_init = true;
@@ -376,124 +337,6 @@ ERROR:
return false;
}
-static void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
- // ignore PNG warnings
-}
-
-/* Get the width and height of a png image */
-int PSPKeyboard::getPngImageSize(Common::SeekableReadStream *file, uint32 *png_width, uint32 *png_height, u32 *paletteSize) {
- DEBUG_ENTER_FUNC();
-
- png_structp png_ptr;
- png_infop info_ptr;
- unsigned int sig_read = 0;
- png_uint_32 width, height;
- int bit_depth, color_type, interlace_type;
-
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (png_ptr == NULL) {
- return -1;
- }
- png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);
- info_ptr = png_create_info_struct(png_ptr);
- if (info_ptr == NULL) {
- png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
- return -1;
- }
- // Set the png lib to use our read function
- png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);
-
- png_set_sig_bytes(png_ptr, sig_read);
- png_read_info(png_ptr, info_ptr);
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
- if (color_type & PNG_COLOR_MASK_PALETTE)
- *paletteSize = info_ptr->num_palette;
- else
- *paletteSize = 0;
-
- png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
-
- *png_width = width;
- *png_height = height;
-
- return 0;
-}
-
-// Load a texture from a png image
-//
-int PSPKeyboard::loadPngImage(Common::SeekableReadStream *file, Buffer &buffer, Palette &palette) {
- DEBUG_ENTER_FUNC();
-
- png_structp png_ptr;
- png_infop info_ptr;
- unsigned int sig_read = 0;
- png_uint_32 width, height;
- int bit_depth, color_type, interlace_type;
- size_t y;
-
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (png_ptr == NULL) {
- PSP_ERROR("Couldn't create read struct to load keyboard\n");
- return -1;
- }
- // Use dummy error function
- png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);
-
- info_ptr = png_create_info_struct(png_ptr);
- if (info_ptr == NULL) {
- PSP_ERROR("Couldn't create info struct to load keyboard\n");
- png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
- return -1;
- }
-
- // Set the png lib to use our customized read function
- png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);
-
- png_set_sig_bytes(png_ptr, sig_read);
- png_read_info(png_ptr, info_ptr);
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
-
- // Strip off 16 bit channels. Not really needed but whatever
- png_set_strip_16(png_ptr);
-
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
- // Copy the palette
- png_colorp srcPal = info_ptr->palette;
- for (int i = 0; i < info_ptr->num_palette; i++) {
- unsigned char alphaVal = (i < info_ptr->num_trans) ? info_ptr->trans[i] : 0xFF; // Load alpha if it's there
- palette.setSingleColorRGBA(i, srcPal->red, srcPal->green, srcPal->blue, alphaVal);
- srcPal++;
- }
- } else { // Not a palettized image
- // Round up grayscale images
- if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr);
- // Convert trans channel to alpha for 32 bits
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
- // Filler for alpha?
- png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
- }
-
- unsigned char *line = (unsigned char*) malloc(info_ptr->rowbytes);
- if (!line) {
- png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
- PSP_ERROR("Couldn't allocate line\n");
- return -1;
- }
-
- for (y = 0; y < height; y++) {
- png_read_row(png_ptr, line, png_bytep_NULL);
- buffer.copyFromRect(line, info_ptr->rowbytes, 0, y, width, 1); // Copy into buffer
- //memcpy(buffer.getPixels()[y * buffer.getWidthInBytes()], line, info_ptr->rowbytes);
- }
-
- free(line);
-
- png_read_end(png_ptr, info_ptr);
- png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
-
- return 0;
-}
-
// Defines for working with PSP buttons
#define CHANGED(x) (_buttonsChanged & (x))
#define PRESSED(x) ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
@@ -508,10 +351,11 @@ int PSPKeyboard::loadPngImage(Common::SeekableReadStream *file, Buffer &buffer,
* Uses the state machine.
* returns whether we have an event
*/
-bool PSPKeyboard::processInput(Common::Event &event, SceCtrlData &pad) {
+bool PSPKeyboard::processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
bool haveEvent = false; // Whether we have an event for the event manager to process
+ bool havePspEvent = false;
event.kbd.flags = 0;
_buttonsChanged = _prevButtons ^ pad.Buttons;
@@ -530,11 +374,11 @@ bool PSPKeyboard::processInput(Common::Event &event, SceCtrlData &pad) {
haveEvent = true;
_dirty = true;
if (UP(PSP_CTRL_START))
- _state = kInvisible; // Make us invisible if unpressed
+ havePspEvent = true;
}
// Check for being in state of moving the keyboard onscreen or pressing select
else if (_state == kMove)
- handleMoveState(pad);
+ havePspEvent = handleMoveState(pad);
else if (_state == kDefault)
haveEvent = handleDefaultState(event, pad);
else if (_state == kCornersSelected)
@@ -544,12 +388,16 @@ bool PSPKeyboard::processInput(Common::Event &event, SceCtrlData &pad) {
else if (_state == kLTriggerDown)
handleLTriggerDownState(pad); // Deal with trigger states
+ if (havePspEvent) {
+ pspEvent.type = PSP_EVENT_SHOW_VIRTUAL_KB; // tell the input handler we're off
+ pspEvent.data = false;
+ }
_prevButtons = pad.Buttons;
return haveEvent;
}
-void PSPKeyboard::handleMoveState(SceCtrlData &pad) {
+bool PSPKeyboard::handleMoveState(SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
if (UP(PSP_CTRL_SELECT)) {
// Toggle between visible and invisible
@@ -560,6 +408,9 @@ void PSPKeyboard::handleMoveState(SceCtrlData &pad) {
_state = kDefault;
_moved = false; // reset moved flag
}
+ if (_state == kInvisible) {
+ return true; // we become invisible
+ }
} else if (DOWN(PSP_DPAD)) { // How we move the KB onscreen
_moved = true;
_dirty = true;
@@ -573,6 +424,7 @@ void PSPKeyboard::handleMoveState(SceCtrlData &pad) {
else /* DOWN(PSP_CTRL_RIGHT) */
increaseKeyboardLocationX(5);
}
+ return false;
}
bool PSPKeyboard::handleDefaultState(Common::Event &event, SceCtrlData &pad) {
diff --git a/backends/platform/psp/pspkeyboard.h b/backends/platform/psp/pspkeyboard.h
index a30e7d0f32..ebf21cfd54 100644
--- a/backends/platform/psp/pspkeyboard.h
+++ b/backends/platform/psp/pspkeyboard.h
@@ -29,12 +29,15 @@
#include "common/events.h"
#include "common/stream.h"
#include "backends/platform/psp/display_client.h"
+//#include "backends/platform/psp/input.h"
#include <pspctrl.h>
//number of modes
#define MODE_COUNT 4
#define guiStringsSize 8 /* size of guistrings array */
+class PspEvent;
+
class PSPKeyboard : public DisplayClient {
private:
@@ -58,10 +61,9 @@ public:
void setClean() { _dirty = false; }
bool isVisible() const { return _state != kInvisible; } // Check if visible
void setVisible(bool val);
- bool processInput(Common::Event &event, SceCtrlData &pad); // Process input
+ bool processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad); // Process input
void moveTo(const int newX, const int newY); // Move keyboard
void render(); // Draw the keyboard onscreen
-
private:
enum CursorDirections {
kUp = 0,
@@ -75,14 +77,11 @@ private:
Palette _palettes[guiStringsSize];
GuRenderer _renderer;
- int loadPngImage(Common::SeekableReadStream *file, Buffer &buffer, Palette &palette);
- int getPngImageSize(Common::SeekableReadStream *, uint32 *png_width, uint32 *png_height, uint32 *paletteSize);
- uint32 convert_pow2(uint32 size);
void increaseKeyboardLocationX(int amount); // Move keyboard onscreen
void increaseKeyboardLocationY(int amount);
void convertCursorToXY(CursorDirections cur, int &x, int &y);
- void handleMoveState(SceCtrlData &pad);
+ bool handleMoveState(SceCtrlData &pad);
bool handleDefaultState(Common::Event &event, SceCtrlData &pad);
bool handleCornersSelectedState(Common::Event &event, SceCtrlData &pad);
bool getInputChoice(Common::Event &event, SceCtrlData &pad);
diff --git a/backends/platform/psp/rtc.cpp b/backends/platform/psp/rtc.cpp
index 57edea7e49..f26e5770a6 100644
--- a/backends/platform/psp/rtc.cpp
+++ b/backends/platform/psp/rtc.cpp
@@ -23,13 +23,13 @@
*
*/
-#include <time.h>
+#include <time.h>
#include <psptypes.h>
#include <psprtc.h>
-
-#include "common/scummsys.h"
-#include "backends/platform/psp/rtc.h"
-
+
+#include "common/scummsys.h"
+#include "backends/platform/psp/rtc.h"
+
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
@@ -51,13 +51,13 @@ void PspRtc::init() { // init our starting ticks
#define MS_LOOP_AROUND 4294967 /* We loop every 2^32 / 1000 = 71 minutes */
#define MS_LOOP_CHECK 60000 /* Threading can cause weird mixups without this */
-// Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause
+// Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause
// unpredictable results
uint32 PspRtc::getMillis() {
uint32 ticks[2];
-
+
sceRtcGetCurrentTick((u64 *)ticks); // can introduce weird thread delays
-
+
uint32 millis = ticks[0]/1000;
millis -= _startMillis; // get ms since start of program
@@ -66,22 +66,22 @@ uint32 PspRtc::getMillis() {
_looped = true;
_milliOffset += MS_LOOP_AROUND; // add the needed offset
PSP_DEBUG_PRINT("looping around. last ms[%d], curr ms[%d]\n", _lastMillis, millis);
- }
+ }
} else {
_looped = false;
}
-
- _lastMillis = millis;
-
+
+ _lastMillis = millis;
+
return millis + _milliOffset;
}
uint32 PspRtc::getMicros() {
uint32 ticks[2];
-
+
sceRtcGetCurrentTick((u64 *)ticks);
ticks[0] -= _startMicros;
-
- return ticks[0];
+
+ return ticks[0];
}
diff --git a/backends/platform/psp/rtc.h b/backends/platform/psp/rtc.h
index 7c1a28474d..841d636e2b 100644
--- a/backends/platform/psp/rtc.h
+++ b/backends/platform/psp/rtc.h
@@ -27,7 +27,7 @@
#define _PSP_RTC_H_
#include "common/singleton.h"
-
+
class PspRtc : public Common::Singleton<PspRtc> {
private:
uint32 _startMillis;
@@ -47,4 +47,4 @@ public:
uint32 getMicros();
};
-#endif \ No newline at end of file
+#endif
diff --git a/backends/platform/psp/tests.cpp b/backends/platform/psp/tests.cpp
index f65c13ae83..4064ee1d98 100644
--- a/backends/platform/psp/tests.cpp
+++ b/backends/platform/psp/tests.cpp
@@ -23,13 +23,13 @@
*
*/
-// PSP speed and unit tests. Activate in tests.h
-// You may also want to build without any engines.
-
+// PSP speed and unit tests. Activate in tests.h
+// You may also want to build without any engines.
+
#include "backends/platform/psp/tests.h"
-#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
-
+#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
+
#include "common/scummsys.h"
#include <pspiofilemgr_fcntl.h>
#include <pspiofilemgr_stat.h>
@@ -49,16 +49,16 @@
#define UNCACHED(x) ((byte *)(((uint32)(x)) | 0x40000000)) /* make an uncached access */
#define CACHED(x) ((byte *)(((uint32)(x)) & 0xBFFFFFFF)) /* make an uncached access into a cached one */
-
+
//#define __PSP_DEBUG_FUNCS__
//#define __PSP_DEBUG_PRINT__
-
+
// Results: (333Mhz/222Mhz)
// Getting a tick: 1-2 us
// Getting a time structure: 9/14us
// ie. using a tick and just dividing by 1000 saves us time.
-
-#include "backends/platform/psp/trace.h"
+
+#include "backends/platform/psp/trace.h"
class PspSpeedTests {
public:
@@ -67,11 +67,11 @@ public:
void seekSpeed();
void msReadSpeed();
void threadFunctionsSpeed();
- void semaphoreSpeed();
+ void semaphoreSpeed();
static int threadFunc(SceSize args, void *argp);
void semaphoreManyThreadSpeed();
void fastCopySpeed();
-
+
private:
enum {
MEMCPY_BUFFER_SIZE = 8192
@@ -95,16 +95,16 @@ void PspSpeedTests::tickSpeed() {
uint32 currentTicks1[2];
uint32 currentTicks2[2];
-
+
sceRtcGetCurrentTick((u64 *)currentTicks1);
sceRtcGetCurrentTick((u64 *)currentTicks2);
PSP_INFO_PRINT("current tick[%x %x][%u %u]\n", currentTicks1[0], currentTicks1[1], currentTicks1[0], currentTicks1[1]);
PSP_INFO_PRINT("current tick[%x %x][%u %u]\n", currentTicks2[0], currentTicks2[1], currentTicks2[0], currentTicks2[1]);
-
+
pspTime time;
sceRtcSetTick(&time, (u64 *)currentTicks2);
- PSP_INFO_PRINT("current tick in time, year[%d] month[%d] day[%d] hour[%d] minutes[%d] seconds[%d] us[%d]\n", time.year, time.month, time.day, time.hour, time.minutes, time.seconds, time.microseconds);
-
+ PSP_INFO_PRINT("current tick in time, year[%d] month[%d] day[%d] hour[%d] minutes[%d] seconds[%d] us[%d]\n", time.year, time.month, time.day, time.hour, time.minutes, time.seconds, time.microseconds);
+
pspTime time1;
pspTime time2;
sceRtcGetCurrentClockLocalTime(&time1);
@@ -119,7 +119,7 @@ void PspSpeedTests::getMicrosSpeed() {
time2 = PspRtc::instance().getMicros();
time3 = PspRtc::instance().getMicros();
time4 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("getMicros() times: %d, %d, %d\n", time4-time3, time3-time2, time2-time1);
}
@@ -128,8 +128,8 @@ void PspSpeedTests::readAndTime(uint32 bytes, char *buffer, FILE *file) {
// test minimal read
fread(buffer, bytes, 1, file);
uint32 time2 = PspRtc::instance().getMicros();
-
- PSP_INFO_PRINT("Reading %d byte takes %dus\n", bytes, time2-time1);
+
+ PSP_INFO_PRINT("Reading %d byte takes %dus\n", bytes, time2-time1);
}
/*
@@ -158,7 +158,7 @@ void PspSpeedTests::msReadSpeed() {
file = fopen("ms0:/psp/music/track1.mp3", "r");
char *buffer = (char *)malloc(2 * 1024 * 1024);
-
+
readAndTime(1, buffer, file);
readAndTime(10, buffer, file);
readAndTime(50, buffer, file);
@@ -170,32 +170,32 @@ void PspSpeedTests::msReadSpeed() {
readAndTime(6000, buffer, file);
readAndTime(7000, buffer, file);
readAndTime(8000, buffer, file);
- readAndTime(9000, buffer, file);
+ readAndTime(9000, buffer, file);
readAndTime(10000, buffer, file);
readAndTime(30000, buffer, file);
readAndTime(50000, buffer, file);
readAndTime(80000, buffer, file);
readAndTime(100000, buffer, file);
-
+
fclose(file);
- free(buffer);
+ free(buffer);
}
-
+
void PspSpeedTests::seekAndTime(int bytes, int origin, FILE *file) {
char buffer[1000];
-
+
uint32 time1 = PspRtc::instance().getMicros();
// test minimal read
fseek(file, bytes, origin);
uint32 time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("Seeking %d byte from %d took %dus\n", bytes, origin, time2-time1);
time1 = PspRtc::instance().getMicros();
// test minimal read
fread(buffer, 1000, 1, file);
time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("Reading 1000 bytes took %dus\n", time2-time1);
}
@@ -246,9 +246,9 @@ int PspSpeedTests::getThreadIdSpeed() {
uint32 time1 = PspRtc::instance().getMicros();
int threadId = sceKernelGetThreadId();
uint32 time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("Getting thread ID %d took %dus\n", threadId, time2-time1);
-
+
return threadId;
}
@@ -257,7 +257,7 @@ void PspSpeedTests::getPrioritySpeed() {
uint32 time1 = PspRtc::instance().getMicros();
int priority = sceKernelGetThreadCurrentPriority();
uint32 time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("Getting thread priority %d took %dus\n", priority, time2-time1);
}
@@ -266,7 +266,7 @@ void PspSpeedTests::changePrioritySpeed(int id, int priority) {
uint32 time1 = PspRtc::instance().getMicros();
sceKernelChangeThreadPriority(id, priority);
uint32 time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("Changing thread priority to %d for id %d took %dus\n", priority, id, time2-time1);
}
@@ -280,53 +280,53 @@ void PspSpeedTests::threadFunctionsSpeed() {
changePrioritySpeed(id, 30);
changePrioritySpeed(id, 35);
changePrioritySpeed(id, 25);
-
+
// test context switch time
for (int i=0; i<10; i++) {
uint time1 = PspRtc::instance().getMicros();
PspThread::delayMicros(0);
uint time2 = PspRtc::instance().getMicros();
- PSP_INFO_PRINT("poll %d. context switch Time = %dus\n", i, time2-time1); // 10-15us
- }
+ PSP_INFO_PRINT("poll %d. context switch Time = %dus\n", i, time2-time1); // 10-15us
+ }
}
-
-void PspSpeedTests::semaphoreSpeed() {
+
+void PspSpeedTests::semaphoreSpeed() {
PspSemaphore sem(1);
-
+
uint32 time1 = PspRtc::instance().getMicros();
-
+
sem.take();
-
+
uint32 time2 = PspRtc::instance().getMicros();
-
+
PSP_INFO_PRINT("taking semaphore took %d us\n", time2-time1); // 10us
-
+
uint32 time3 = PspRtc::instance().getMicros();
-
+
sem.give();
-
+
uint32 time4 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("releasing semaphore took %d us\n", time4-time3); //10us-55us
}
int PspSpeedTests::threadFunc(SceSize args, void *argp) {
PSP_INFO_PRINT("thread %x created.\n", sceKernelGetThreadId());
-
+
_sem.take();
-
+
PSP_INFO_PRINT("grabbed semaphore. Quitting thread\n");
-
+
return 0;
}
-void PspSpeedTests::semaphoreManyThreadSpeed() {
-
+void PspSpeedTests::semaphoreManyThreadSpeed() {
+
// create 4 threads
for (int i=0; i<4; i++) {
int thid = sceKernelCreateThread("my_thread", PspSpeedTests::threadFunc, 0x18, 0x10000, THREAD_ATTR_USER, NULL);
sceKernelStartThread(thid, 0, 0);
}
-
+
PSP_INFO_PRINT("main thread. created threads\n");
uint32 threads = _sem.numOfWaitingThreads();
@@ -334,10 +334,10 @@ void PspSpeedTests::semaphoreManyThreadSpeed() {
threads = _sem.numOfWaitingThreads();
PSP_INFO_PRINT("main thread: waiting threads[%d]\n", threads);
}
-
+
PSP_INFO_PRINT("main: semaphore value[%d]\n", _sem.getValue());
PSP_INFO_PRINT("main thread: waiting threads[%d]\n", _sem.numOfWaitingThreads());
-
+
_sem.give(4);
}
@@ -346,31 +346,31 @@ void PspSpeedTests::fastCopySpecificSize(byte *dst, byte *src, uint32 bytes) {
uint32 fastcopyTime, memcpyTime;
const int iterations = 2000;
int intc;
-
+
intc = pspSdkDisableInterrupts();
-
+
time1 = PspRtc::instance().getMicros();
for (int i=0; i<iterations; i++) {
PspMemory::fastCopy(dst, src, bytes);
- }
+ }
time2 = PspRtc::instance().getMicros();
-
+
pspSdkEnableInterrupts(intc);
-
+
fastcopyTime = time2-time1;
-
+
intc = pspSdkDisableInterrupts();
-
+
time1 = PspRtc::instance().getMicros();
for (int i=0; i<iterations; i++) {
memcpy(dst, src, bytes);
- }
+ }
time2 = PspRtc::instance().getMicros();
-
+
pspSdkEnableInterrupts(intc);
-
+
memcpyTime = time2-time1;
-
+
PSP_INFO_PRINT("%d bytes. memcpy[%d], fastcopy[%d]\n", bytes, memcpyTime, fastcopyTime);
}
@@ -397,16 +397,16 @@ void PspSpeedTests::fastCopySpeed() {
uint32 *bufferSrc32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
uint32 *bufferDst32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
-
+
// fill buffer 1
for (int i=0; i<MEMCPY_BUFFER_SIZE/4; i++)
bufferSrc32[i] = i | (((MEMCPY_BUFFER_SIZE/4)-i)<<16);
-
+
// print buffer
for (int i=0; i<50; i++)
PSP_INFO_PRINT("%x ", bufferSrc32[i]);
PSP_INFO_PRINT("\n");
-
+
byte *bufferSrc = ((byte *)bufferSrc32);
byte *bufferDst = ((byte *)bufferDst32);
@@ -415,7 +415,7 @@ void PspSpeedTests::fastCopySpeed() {
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
-
+
PSP_INFO_PRINT("\n\ndst cached, src uncached: -----------------\n");
bufferSrc = UNCACHED(bufferSrc);
fastCopyDifferentSizes(bufferDst, bufferSrc);
@@ -429,7 +429,7 @@ void PspSpeedTests::fastCopySpeed() {
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
-
+
PSP_INFO_PRINT("\n\ndst uncached, src cached: -------------------\n");
bufferSrc = CACHED(bufferSrc);
fastCopyDifferentSizes(bufferDst, bufferSrc);
@@ -437,7 +437,7 @@ void PspSpeedTests::fastCopySpeed() {
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
-
+
free(bufferSrc32);
free(bufferDst32);
}
@@ -453,11 +453,11 @@ private:
enum {
MEMCPY_BUFFER_SIZE = 8192
};
-
+
void fastCopySpecificSize(byte *dst, byte *src, uint32 bytes, bool swap = false);
void fastCopyDifferentSizes(byte *dst, byte *src, bool swap = false);
-
-};
+
+};
void PspUnitTests::testFastCopy() {
PSP_INFO_PRINT("running fastcopy unit test ***********\n");
@@ -465,19 +465,19 @@ void PspUnitTests::testFastCopy() {
uint32 *bufferSrc32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
uint32 *bufferDst32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
-
+
// fill buffer 1
for (int i=0; i<MEMCPY_BUFFER_SIZE/4; i++)
bufferSrc32[i] = i | (((MEMCPY_BUFFER_SIZE/4)-i)<<16);
-
+
// print buffer
for (int i=0; i<50; i++)
PSP_INFO_PRINT("%x ", bufferSrc32[i]);
PSP_INFO_PRINT("\n");
-
+
byte *bufferSrc = ((byte *)bufferSrc32);
byte *bufferDst = ((byte *)bufferDst32);
-
+
fastCopyDifferentSizes(bufferDst, bufferSrc, true);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+2, bufferSrc+2, true);
@@ -491,12 +491,12 @@ void PspUnitTests::testFastCopy() {
fastCopyDifferentSizes(bufferDst+2, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+2, bufferSrc+3);
fastCopyDifferentSizes(bufferDst+3, bufferSrc+1);
- fastCopyDifferentSizes(bufferDst+3, bufferSrc+2);
-
+ fastCopyDifferentSizes(bufferDst+3, bufferSrc+2);
+
free(bufferSrc32);
free(bufferDst32);
}
-
+
void PspUnitTests::fastCopyDifferentSizes(byte *dst, byte *src, bool swap) {
fastCopySpecificSize(dst, src, 1);
fastCopySpecificSize(dst, src, 2, swap);
@@ -509,7 +509,7 @@ void PspUnitTests::fastCopyDifferentSizes(byte *dst, byte *src, bool swap) {
fastCopySpecificSize(dst, src, 12, swap);
fastCopySpecificSize(dst, src, 13);
fastCopySpecificSize(dst, src, 14, swap);
- fastCopySpecificSize(dst, src, 15);
+ fastCopySpecificSize(dst, src, 15);
fastCopySpecificSize(dst, src, 16, swap);
fastCopySpecificSize(dst, src, 17);
fastCopySpecificSize(dst, src, 18, swap);
@@ -529,16 +529,16 @@ void PspUnitTests::fastCopyDifferentSizes(byte *dst, byte *src, bool swap) {
void PspUnitTests::fastCopySpecificSize(byte *dst, byte *src, uint32 bytes, bool swap) {
memset(dst, 0, bytes);
PspMemory::fastCopy(dst, src, bytes);
-
+
if (swap) { // test swap also
memset(dst, 0, bytes);
-
+
// pixelformat for swap
PSPPixelFormat format;
format.set(PSPPixelFormat::Type_4444, true);
-
+
PspMemory::fastSwap(dst, src, bytes, format);
- }
+ }
}
// This function leaks. For now I don't care
@@ -549,23 +549,23 @@ bool PspUnitTests::testFileSystem() {
int i;
Common::WriteStream *wrStream;
Common::SeekableReadStream *rdStream;
-
+
PSP_INFO_PRINT("testing fileSystem...\n");
-
+
// fill buffer
for (i=0; i<(int)BufSize; i += 4) {
buffer[i] = 'A';
buffer[i + 1] = 'B';
buffer[i + 2] = 'C';
buffer[i + 3] = 'D';
- }
-
+ }
+
// create a file
const char *path = "./file.test";
Common::FSNode file(path);
-
+
PSP_INFO_PRINT("creating write stream...\n");
-
+
wrStream = file.createWriteStream();
if (!wrStream) {
PSP_ERROR("%s couldn't be created.\n", path);
@@ -576,9 +576,9 @@ bool PspUnitTests::testFileSystem() {
char* index = buffer;
int32 totalLength = BufSize;
int32 curLength = 50;
-
+
PSP_INFO_PRINT("writing...\n");
-
+
while(totalLength - curLength > 0) {
if ((int)wrStream->write(index, curLength) != curLength) {
PSP_ERROR("couldn't write %d bytes\n", curLength);
@@ -595,22 +595,22 @@ bool PspUnitTests::testFileSystem() {
PSP_ERROR("couldn't write %d bytes\n", curLength);
return false;
}
-
+
delete wrStream;
-
+
PSP_INFO_PRINT("reading...\n");
-
+
rdStream = file.createReadStream();
if (!rdStream) {
PSP_ERROR("%s couldn't be created.\n", path);
return false;
}
-
+
// seek to beginning
if (!rdStream->seek(0, SEEK_SET)) {
PSP_ERROR("couldn't seek to the beginning after writing the file\n");
return false;
- }
+ }
// read the contents
char *readBuffer = new char[BufSize + 4];
@@ -619,96 +619,96 @@ bool PspUnitTests::testFileSystem() {
while (rdStream->read(index, 100) == 100) {
index += 100;
}
-
+
if (!rdStream->eos()) {
PSP_ERROR("didn't find EOS at end of stream\n");
- return false;
+ return false;
}
-
+
// compare
for (i=0; i<(int)BufSize; i++)
if (buffer[i] != readBuffer[i]) {
PSP_ERROR("reading/writing mistake at %x. Got %x instead of %x\n", i, readBuffer[i], buffer[i]);
return false;
}
-
+
// Check for exceeding limit
for (i=0; i<4; i++) {
if (readBuffer[BufSize + i]) {
PSP_ERROR("read exceeded limits. %d = %x\n", BufSize + i, readBuffer[BufSize + i]);
}
- }
-
+ }
+
delete rdStream;
-
+
PSP_INFO_PRINT("writing...\n");
-
+
wrStream = file.createWriteStream();
if (!wrStream) {
PSP_ERROR("%s couldn't be created.\n", path);
return false;
}
-
+
const char *phrase = "Jello is really fabulous";
uint32 phraseLen = strlen(phrase);
-
+
int ret;
if ((ret = wrStream->write(phrase, phraseLen)) != (int)phraseLen) {
PSP_ERROR("couldn't write phrase. Got %d instead of %d\n", ret, phraseLen);
return false;
}
-
+
PSP_INFO_PRINT("reading...\n");
-
+
delete wrStream;
rdStream = file.createReadStream();
if (!rdStream) {
PSP_ERROR("%s couldn't be created.\n", path);
return false;
}
-
+
char *readPhrase = new char[phraseLen + 2];
memset(readPhrase, 0, phraseLen + 2);
-
+
if ((ret = rdStream->read(readPhrase, phraseLen) != phraseLen)) {
PSP_ERROR("read error on phrase. Got %d instead of %d\n", ret, phraseLen);
return false;
}
-
+
for (i=0; i<(int)phraseLen; i++) {
if (readPhrase[i] != phrase[i]) {
PSP_ERROR("bad read/write in phrase. At %d, %x != %x\n", i, readPhrase[i], phrase[i]);
return false;
}
- }
-
+ }
+
// check for exceeding
if (readPhrase[i] != 0) {
PSP_ERROR("found excessive copy in phrase. %c at %d\n", readPhrase[i], i);
return false;
}
-
+
PSP_INFO_PRINT("trying to read end...\n");
-
+
// seek to end
if (!rdStream->seek(0, SEEK_END)) {
PSP_ERROR("couldn't seek to end for append\n");
return false;
};
-
+
// try to read
if (rdStream->read(readPhrase, 2) || !rdStream->eos()) {
PSP_ERROR("was able to read at end of file\n");
return false;
}
-
+
PSP_INFO_PRINT("ok\n");
return true;
}
void psp_tests() {
PSP_INFO_PRINT("in tests\n");
-
+
#ifdef PSP_ENABLE_SPEED_TESTS
// Speed tests
PspSpeedTests speedTests;
@@ -718,18 +718,18 @@ void psp_tests() {
speedTests.seekSpeed();
speedTests.msReadSpeed();
speedTests.threadFunctionsSpeed();
- speedTests.semaphoreSpeed();
+ speedTests.semaphoreSpeed();
speedTests.semaphoreManyThreadSpeed();
speedTests.fastCopySpeed();
-#endif
-
+#endif
+
#ifdef PSP_ENABLE_UNIT_TESTS
// Unit tests
PspUnitTests unitTests;
-
+
//unitTests.testFastCopy();
unitTests.testFileSystem();
-#endif
-}
+#endif
+}
-#endif /* (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS) */ \ No newline at end of file
+#endif /* (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS) */
diff --git a/backends/platform/psp/tests.h b/backends/platform/psp/tests.h
index 1518acfb4c..9af7ba8d7d 100644
--- a/backends/platform/psp/tests.h
+++ b/backends/platform/psp/tests.h
@@ -23,14 +23,14 @@
*
*/
-#ifndef _PSP_TESTS_H_
+#ifndef _PSP_TESTS_H_
#define _PSP_TESTS_H_
//#define PSP_ENABLE_UNIT_TESTS // run unit tests
//#define PSP_ENABLE_SPEED_TESTS // run speed tests
-#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
+#if defined (PSP_ENABLE_UNIT_TESTS) || defined (PSP_ENABLE_SPEED_TESTS)
void psp_tests();
#endif
-#endif /* _PSP_TESTS_H_ */ \ No newline at end of file
+#endif /* _PSP_TESTS_H_ */
diff --git a/backends/platform/psp/thread.cpp b/backends/platform/psp/thread.cpp
index e757c2f575..3e1303a4e6 100644
--- a/backends/platform/psp/thread.cpp
+++ b/backends/platform/psp/thread.cpp
@@ -23,12 +23,12 @@
*
*/
-#include <pspthreadman.h>
+#include <pspthreadman.h>
#include "backends/platform/psp/thread.h"
#include "backends/platform/psp/trace.h"
-
-// Class PspThreadable --------------------------------------------------
+
+// Class PspThreadable --------------------------------------------------
// Inherit this to create C++ threads easily
bool PspThreadable::threadCreateAndStart(const char *threadName, int priority, int stackSize, bool useVfpu /*= false*/) {
@@ -38,41 +38,41 @@ bool PspThreadable::threadCreateAndStart(const char *threadName, int priority, i
PSP_ERROR("thread already created!\n");
return false;
}
-
+
_threadId = sceKernelCreateThread(threadName, __threadCallback, priority, stackSize, THREAD_ATTR_USER, 0); // add VFPU support
if (_threadId < 0) {
PSP_ERROR("failed to create %s thread. Error code %d\n", threadName, _threadId);
return false;
}
-
+
// We want to pass the pointer to this, but we'll have to take address of this so use a little trick
PspThreadable *_this = this;
-
+
if (sceKernelStartThread(_threadId, sizeof(uint32 *), &_this) < 0) {
PSP_ERROR("failed to start %s thread id[%d]\n", threadName, _threadId);
return false;
}
-
- PSP_DEBUG_PRINT("Started %s thread with id[%x]\n", threadName, _threadId);
-
+
+ PSP_DEBUG_PRINT("Started %s thread with id[%x]\n", threadName, _threadId);
+
return true;
}
// Callback function to be called by PSP kernel
int PspThreadable::__threadCallback(SceSize, void *__this) {
DEBUG_ENTER_FUNC();
-
+
PspThreadable *_this = *(PspThreadable **)__this; // Dereference the copied value which was 'this'
-
+
_this->threadFunction(); // call the virtual function
-
+
return 0;
}
// PspThread class
// Utilities to access general thread functions
-
+
void PspThread::delayMillis(uint32 ms) {
sceKernelDelayThread(ms * 1000);
}
@@ -90,8 +90,8 @@ void PspThread::delayMicros(uint32 us) {
PspSemaphore::PspSemaphore(int initialValue, int maxValue/*=255*/) {
DEBUG_ENTER_FUNC();
_handle = 0;
- _handle = (uint32)sceKernelCreateSema("ScummVM Sema", 0 /* attr */,
- initialValue, maxValue,
+ _handle = (uint32)sceKernelCreateSema("ScummVM Sema", 0 /* attr */,
+ initialValue, maxValue,
0 /*option*/);
if (!_handle)
PSP_ERROR("failed to create semaphore.\n");
@@ -108,10 +108,10 @@ int PspSemaphore::numOfWaitingThreads() {
DEBUG_ENTER_FUNC();
SceKernelSemaInfo info;
info.numWaitThreads = 0;
-
+
if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
-
+
return info.numWaitThreads;
}
@@ -119,10 +119,10 @@ int PspSemaphore::getValue() {
DEBUG_ENTER_FUNC();
SceKernelSemaInfo info;
info.currentCount = 0;
-
+
if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
-
+
return info.currentCount;
}
@@ -130,18 +130,18 @@ bool PspSemaphore::pollForValue(int value) {
DEBUG_ENTER_FUNC();
if (sceKernelPollSema((SceUID)_handle, value) < 0)
return false;
-
+
return true;
}
// false: timeout or error
bool PspSemaphore::takeWithTimeOut(uint32 timeOut) {
DEBUG_ENTER_FUNC();
-
+
uint32 *pTimeOut = 0;
- if (timeOut)
+ if (timeOut)
pTimeOut = &timeOut;
-
+
if (sceKernelWaitSema(_handle, 1, pTimeOut) < 0) // we always wait for 1
return false;
return true;
@@ -149,9 +149,9 @@ bool PspSemaphore::takeWithTimeOut(uint32 timeOut) {
bool PspSemaphore::give(int num /*=1*/) {
DEBUG_ENTER_FUNC();
-
+
if (sceKernelSignalSema((SceUID)_handle, num) < 0)
- return false;
+ return false;
return true;
}
@@ -161,7 +161,7 @@ bool PspMutex::lock() {
DEBUG_ENTER_FUNC();
int threadId = sceKernelGetThreadId();
bool ret = true;
-
+
if (_ownerId == threadId) {
_recursiveCount++;
} else {
@@ -176,19 +176,19 @@ bool PspMutex::unlock() {
DEBUG_ENTER_FUNC();
int threadId = sceKernelGetThreadId();
bool ret = true;
-
+
if (_ownerId != threadId) {
PSP_ERROR("attempt to unlock mutex by thread[%x] as opposed to owner[%x]\n",
threadId, _ownerId);
return false;
}
-
+
if (_recursiveCount) {
_recursiveCount--;
} else {
_ownerId = 0;
ret = _semaphore.give(1);
- }
+ }
return ret;
}
@@ -200,7 +200,7 @@ void PspCondition::releaseAll() {
if (_waitingThreads > _signaledThreads) { // we have signals to issue
int numWaiting = _waitingThreads - _signaledThreads; // threads we haven't signaled
_signaledThreads = _waitingThreads;
-
+
_waitSem.give(numWaiting);
_mutex.unlock();
for (int i=0; i<numWaiting; i++) // wait for threads to tell us they're awake
diff --git a/backends/platform/psp/thread.h b/backends/platform/psp/thread.h
index 44394af40a..1ca6ef5c42 100644
--- a/backends/platform/psp/thread.h
+++ b/backends/platform/psp/thread.h
@@ -34,7 +34,7 @@ class PspThreadable {
protected:
int _threadId;
virtual void threadFunction() = 0; // this function will be called when the thread starts
-public:
+public:
PspThreadable() : _threadId(-1) {} // constructor
virtual ~PspThreadable() {} // destructor
static int __threadCallback(SceSize, void *__this); // used to get called by sceKernelStartThread() Don't override
@@ -43,7 +43,7 @@ public:
// class for thread utils
class PspThread {
-public:
+public:
// static functions
static void delayMillis(uint32 ms); // delay the current thread
static void delayMicros(uint32 us);
@@ -106,7 +106,7 @@ enum StackSizes {
STACK_DISPLAY_THREAD = 2 * 1024,
STACK_POWER_THREAD = 4 * 1024
};
-
+
#endif /* PSP_THREADS_H */
diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp
index 9bf71d1c33..df63c3a7d6 100644
--- a/backends/platform/sdl/graphics.cpp
+++ b/backends/platform/sdl/graphics.cpp
@@ -237,7 +237,7 @@ void OSystem_SDL::detectSupportedFormats() {
// available format, it will get one that is "cheap" to
// use.
const Graphics::PixelFormat RGBList[] = {
-#ifdef ENABLE_32BIT
+#ifdef USE_RGB_COLOR
// RGBA8888, ARGB8888, RGB888
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0),
Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24),
@@ -251,7 +251,7 @@ void OSystem_SDL::detectSupportedFormats() {
Graphics::PixelFormat(2, 4, 4, 4, 4, 8, 4, 0, 12)
};
const Graphics::PixelFormat BGRList[] = {
-#ifdef ENABLE_32BIT
+#ifdef USE_RGB_COLOR
// ABGR8888, BGRA8888, BGR888
Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24),
Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0),
@@ -527,7 +527,7 @@ static void fixupResolutionForAspectRatio(AspectRatio desiredAspectRatio, int &w
}
if (!bestMode) {
- warning("Unable to enforce the desired aspect ratio!");
+ warning("Unable to enforce the desired aspect ratio");
return;
}
//printf("%d %d\n", bestMode->w, bestMode->h);
@@ -539,7 +539,7 @@ bool OSystem_SDL::loadGFXMode() {
assert(_inited);
_forceFull = true;
-#if !defined(__MAEMO__) && !defined(GP2XWIZ) && !defined(LINUXMOTO) && !defined(DINGUX)
+#if !defined(__MAEMO__) && !defined(DINGUX) && !defined(GPH_DEVICE) && !defined(LINUXMOTO) && !defined(OPENPANDORA)
_videoMode.overlayWidth = _videoMode.screenWidth * _videoMode.scaleFactor;
_videoMode.overlayHeight = _videoMode.screenHeight * _videoMode.scaleFactor;
@@ -785,7 +785,7 @@ void OSystem_SDL::internUpdateScreen() {
#endif
// If the shake position changed, fill the dirty area with blackness
- if (_currentShakePos != _newShakePos ||
+ if (_currentShakePos != _newShakePos ||
(_mouseNeedsRedraw && _mouseBackup.y <= _currentShakePos)) {
SDL_Rect blackrect = {0, 0, _videoMode.screenWidth * _videoMode.scaleFactor, _newShakePos * _videoMode.scaleFactor};
diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp
index 9d116d325a..a9e1f5cf4b 100644
--- a/backends/platform/sdl/main.cpp
+++ b/backends/platform/sdl/main.cpp
@@ -23,6 +23,8 @@
*
*/
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
// Fix for bug #2895217 "MSVC compilation broken with r47595":
// We need to keep this on top of the "common/scummsys.h" include,
// otherwise we will get errors about the windows headers redefining
@@ -38,7 +40,7 @@
// Several SDL based ports use a custom main, and hence do not want to compile
// of this file. The following "#if" ensures that.
-#if !defined(__MAEMO__) && !defined(_WIN32_WCE) && !defined(GP2XWIZ)&& !defined(LINUXMOTO) && !defined(__SYMBIAN32__) && !defined(DINGUX)
+#if !defined(__MAEMO__) && !defined(__SYMBIAN32__) && !defined(_WIN32_WCE) && !defined(DINGUX) && !defined(GPH_DEVICE) && !defined(LINUXMOTO) && !defined(OPENPANDORA)
#include "backends/platform/sdl/sdl.h"
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index 22fc561b0f..8725a7df8a 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -79,7 +82,7 @@
#endif
#if defined(MACOSX) || defined(IPHONE)
-#include "CoreFoundation/CoreFoundation.h"
+#include <CoreFoundation/CoreFoundation.h>
#endif
@@ -116,6 +119,26 @@ static AspectRatio getDesiredAspectRatio() {
}
#endif
+#if defined(WIN32)
+ struct SdlConsoleHidingWin32 {
+ DWORD myPid;
+ DWORD myTid;
+ HWND consoleHandle;
+ };
+
+ // console hiding for win32
+ static BOOL CALLBACK initBackendFindConsoleWin32Proc(HWND hWnd, LPARAM lParam) {
+ DWORD pid, tid;
+ SdlConsoleHidingWin32 *variables = (SdlConsoleHidingWin32 *)lParam;
+ tid = GetWindowThreadProcessId(hWnd, &pid);
+ if ((tid == variables->myTid) && (pid == variables->myPid)) {
+ variables->consoleHandle = hWnd;
+ return FALSE;
+ }
+ return TRUE;
+ }
+#endif
+
void OSystem_SDL::initBackend() {
assert(!_inited);
@@ -135,6 +158,25 @@ void OSystem_SDL::initBackend() {
if (joystick_num > -1)
sdlFlags |= SDL_INIT_JOYSTICK;
+#if 0
+ // NEW CODE TO HIDE CONSOLE FOR WIN32
+#if defined(WIN32)
+ // console hiding for win32
+ SdlConsoleHidingWin32 consoleHidingWin32;
+ consoleHidingWin32.consoleHandle = 0;
+ consoleHidingWin32.myPid = GetCurrentProcessId();
+ consoleHidingWin32.myTid = GetCurrentThreadId();
+ EnumWindows (initBackendFindConsoleWin32Proc, (LPARAM)&consoleHidingWin32);
+
+ if (!ConfMan.getBool("show_console")) {
+ if (consoleHidingWin32.consoleHandle) {
+ // We won't find a window with our TID/PID in case we were started from command-line
+ ShowWindow(consoleHidingWin32.consoleHandle, SW_HIDE);
+ }
+ }
+#endif
+#endif
+
if (SDL_Init(sdlFlags) == -1) {
error("Could not initialize SDL: %s", SDL_GetError());
}
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index 5c901ba711..e7f9a06d80 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -412,7 +412,7 @@ protected:
// Scroll lock state - since SDL doesn't track it
bool _scrollLock;
-
+
// joystick
SDL_Joystick *_joystick;
diff --git a/backends/platform/symbian/AdaptAllMMPs.pl b/backends/platform/symbian/AdaptAllMMPs.pl
index 8b7151c180..8786ecff32 100644
--- a/backends/platform/symbian/AdaptAllMMPs.pl
+++ b/backends/platform/symbian/AdaptAllMMPs.pl
@@ -8,41 +8,42 @@ chdir("../../../");
@mmp_files = (
# Engine Project files
- "mmp/scummvm_agi.mmp",
- "mmp/scummvm_agos.mmp",
- "mmp/scummvm_cine.mmp",
- "mmp/scummvm_cruise.mmp",
- "mmp/scummvm_drascula.mmp",
- "mmp/scummvm_gob.mmp",
- "mmp/scummvm_groovie.mmp",
- "mmp/scummvm_kyra.mmp",
- "mmp/scummvm_lure.mmp",
+ "mmp/scummvm_agi.mmp",
+ "mmp/scummvm_agos.mmp",
+ "mmp/scummvm_cine.mmp",
+ "mmp/scummvm_cruise.mmp",
+ "mmp/scummvm_drascula.mmp",
+ "mmp/scummvm_gob.mmp",
+ "mmp/scummvm_groovie.mmp",
+ "mmp/scummvm_kyra.mmp",
+ "mmp/scummvm_lure.mmp",
"mmp/scummvm_m4.mmp",
"mmp/scummvm_made.mmp",
- "mmp/scummvm_parallaction.mmp",
- "mmp/scummvm_queen.mmp",
- "mmp/scummvm_saga.mmp",
- "mmp/scummvm_scumm.mmp",
- "mmp/scummvm_sky.mmp",
- "mmp/scummvm_sword1.mmp",
+ "mmp/scummvm_parallaction.mmp",
+ "mmp/scummvm_queen.mmp",
+ "mmp/scummvm_saga.mmp",
+ "mmp/scummvm_scumm.mmp",
+ "mmp/scummvm_sky.mmp",
+ "mmp/scummvm_sword1.mmp",
"mmp/scummvm_sword2.mmp",
- "mmp/scummvm_touche.mmp",
- "mmp/scummvm_tinsel.mmp",
- "mmp/scummvm_tucker.mmp",
- "mmp/scummvm_sci.mmp",
- "mmp/scummvm_draci.mmp",
- "mmp/scummvm_teenagent.mmp",
- "mmp/scummvm_mohawk.mmp",
- "mmp/scummvm_hugo.mmp",
+ "mmp/scummvm_touche.mmp",
+ "mmp/scummvm_tinsel.mmp",
+ "mmp/scummvm_tucker.mmp",
+ "mmp/scummvm_sci.mmp",
+ "mmp/scummvm_draci.mmp",
+ "mmp/scummvm_teenagent.mmp",
+ "mmp/scummvm_mohawk.mmp",
+ "mmp/scummvm_hugo.mmp",
+ "mmp/scummvm_toon.mmp",
# Target Platform Project Files
- "S60/ScummVM_S60.mmp",
- "S60v3/ScummVM_S60v3.mmp",
- "S60v3/ScummVM_A0000658_S60v3.mmp",
- "S80/ScummVM_S80.mmp",
+ "S60/ScummVM_S60.mmp",
+ "S60v3/ScummVM_S60v3.mmp",
+ "S60v3/ScummVM_A0000658_S60v3.mmp",
+ "S80/ScummVM_S80.mmp",
"S90/ScummVM_S90.mmp",
- "UIQ2/ScummVM_UIQ2.mmp",
+ "UIQ2/ScummVM_UIQ2.mmp",
"UIQ3/ScummVM_UIQ3.mmp",
- "UIQ3/ScummVM_A0000658_UIQ3.mmp"
+ "UIQ3/ScummVM_A0000658_UIQ3.mmp"
);
@@ -75,7 +76,7 @@ my @sections_kyra = ("", "ENABLE_LOL"); # special sections for engine KYRA
my @sections_agos = ("", "ENABLE_AGOS2"); # special sections for engine AGOS
# files excluded from build, case insensitive, will be matched in filename string only
-my @excludes_snd = (
+my @excludes_snd = (
"mt32.*",
"fluidsynth.cpp",
"i386.cpp",
@@ -86,14 +87,14 @@ my @excludes_snd = (
"rate.*" # not really needed, USE_ARM_SOUND_ASM currently not parsed correctly,
# "rate[_arm|_arm_asm].(cpp|s)" will be added later based on WINS/ARM build!
# These #defines for compile time are set in portdefs.h
-);
+);
-my @excludes_graphics = (
+my @excludes_graphics = (
"iff.cpp"
-);
+);
-my @excludes_gui = (
-);
+my @excludes_gui = (
+);
# the USE_ARM_* defines not parsed correctly, exclude manually:
my @excludes_scumm = (
@@ -138,6 +139,7 @@ ParseModule("_draci", "draci", \@section_empty);
ParseModule("_teenagent","teenagent", \@section_empty);
ParseModule("_mohawk" ,"mohawk", \@section_empty);
ParseModule("_hugo" ,"hugo", \@section_empty);
+ParseModule("_toon" ,"toon", \@section_empty);
print "
=======================================================================================
Done. Enjoy :P
@@ -147,7 +149,7 @@ Done. Enjoy :P
##################################################################################################################
##################################################################################################################
-# parses multiple sections per mmp/module
+# parses multiple sections per mmp/module
sub ParseModule
{
my ($mmp,$module,$sections,$exclusions) = @_;
@@ -172,7 +174,7 @@ sub CheckForModuleMK
if (-d $item)
{
#print "$item\n";
-
+
opendir DIR, $item;
#my @Files = readdir DIR;
my @Files = grep s/^([^\.].*)$/$1/, readdir DIR;
@@ -191,7 +193,7 @@ sub CheckForModuleMK
my $isenable;
my $ObjectsSelected = 0;
my $ObjectsTotal = 0;
-
+
print "$item for section '$section' ... ";
open FILE, $item;
@@ -200,14 +202,14 @@ sub CheckForModuleMK
my $count = @lines;
print "$count lines";
-
+
A: foreach $line (@lines)
{
# all things we need are inside #ifdef sections,
# there is nothing we need in #ifndef sections: so ignore these for now
-
- # found a section? reset
- if ($line =~ /^ifdef (.*)/)
+
+ # found a section? reset
+ if ($line =~ /^ifdef (.*)/)
{
$sec = $1;
$isenable = 1;
@@ -230,7 +232,7 @@ sub CheckForModuleMK
$line =~ s/ \\//; # remove possible trailing ' \'
$line =~ s/\//\\/g; # replace / with \
chop($line); # remove \n
-
+
# do we need to skip this file? According to our own @exclusions array
foreach $exclusion (@exclusions)
{
@@ -242,7 +244,7 @@ sub CheckForModuleMK
next A;
}
}
-
+
# do we need to do this file? According to MACROs in .MMPs
my $found = 0;
foreach $EnableDefine (@EnabledDefines)
@@ -270,7 +272,7 @@ sub CheckForModuleMK
$output .= "//SOURCE $line ($reason)\n";
next A;
}
-
+
$ObjectsSelected++;
#print "\n $line";
$output .= "SOURCE $line\n";
@@ -293,7 +295,7 @@ sub UpdateProjectFile
my $updated = " Updated @ ".localtime();
my $name;
my @mmp_files_plus_one = @mmp_files;
- unshift @mmp_files_plus_one, "mmp/scummvm_base.mmp";
+ unshift @mmp_files_plus_one, "mmp/scummvm_base.mmp";
foreach $name (@mmp_files_plus_one)
{
@@ -302,23 +304,23 @@ sub UpdateProjectFile
open FILE, "$file";
my @lines = <FILE>;
close FILE;
-
+
my $onestr = join("",@lines);
-
+
if ($onestr =~ /$n/)
{
print " - $name @ $n updating ... ";
-
+
$onestr =~ s/$a.*$b/$a$updated\n$output$b/s;
open FILE, ">$file";
print FILE $onestr;
close FILE;
-
+
print "done.\n";
}
}
-
+
$output = "";
}
@@ -328,7 +330,7 @@ sub UpdateSlaveMacros
{
my $updated = " Updated @ ".localtime();
- my $name = "mmp/scummvm_base.mmp";
+ my $name = "mmp/scummvm_base.mmp";
my $file = "$buildDir/$name";
print "Reading master MACROS from backends/symbian/$name ... ";
@@ -342,7 +344,7 @@ sub UpdateSlaveMacros
my $b = "\/\/STOP_$n\/\/";
$onestr =~ /$a(.*)$b/s;
my $macros = $1;
-
+
my $libs_first = "\n// automagically enabled static libs from macros above\n";
my $libs_second = "STATICLIBRARY scummvm_base.lib // must be above USE_* .libs\n";
my $macro_counter = 0;
@@ -355,7 +357,7 @@ sub UpdateSlaveMacros
if ($line =~ /^.*MACRO\s*([0-9A-Z_]*)\s*\/\/\s*LIB\:(.*)$/)
{
my $macro = $1; my $lib = $2;
-
+
# this macro enabled? then also add the .lib
if ($line =~ /^\s*MACRO\s*$macro/m)
{
@@ -366,7 +368,7 @@ sub UpdateSlaveMacros
if ($macro =~ /^ENABLE_/)
{
$libs_first .= "STATICLIBRARY $lib\n";
-
+
# add projects for BLD.INF's
my $projectname = substr("$lib",0,-4);
$projects .= "..\\mmp\\$projectname.mmp\n";
@@ -393,7 +395,7 @@ sub UpdateSlaveMacros
push @DisabledDefines, $macro; # used in CheckForModuleMK()!!
}
}
- }
+ }
print "$macro_counter macro lines.\n";
@@ -404,23 +406,23 @@ sub UpdateSlaveMacros
$m = "AUTO_PROJECTS";
$p = "\/\/START_$m\/\/";
$q = "\/\/STOP_$m\/\/";
-
+
foreach $name (@mmp_files)
{
$file = "$buildDir/$name";
$fileBLDINF = $buildDir .'/'. substr($name, 0, rindex($name, "/")) . "/BLD.INF";
print "Updating macros in $file ... ";
#print "Updating macros in backends/symbian/$name ... ";
-
+
open FILE, "$file"; @lines = <FILE>; close FILE;
$onestr = join("",@lines);
-
+
my $extralibs = ""; # output
# slash in name means it's a phone specific build file: add LIBs
$extralibs .= "$libs_first$libs_second" if (-e $fileBLDINF);
-
+
$onestr =~ s/$a.*$b/$a$updated$macros2$extralibs$b/s;
-
+
open FILE, ">$file"; print FILE $onestr; close FILE;
my $count = @lines;
@@ -434,13 +436,13 @@ sub UpdateSlaveMacros
open FILE, "$fileBLDINF"; @lines = <FILE>; close FILE;
$onestr = join("",@lines);
-
+
$onestr =~ s/$p.*$q/$p$updated$projects$q/s;
-
+
open FILE, ">$fileBLDINF"; print FILE $onestr; close FILE;
}
}
-}
+}
##################################################################################################################
@@ -448,10 +450,10 @@ sub ResetProjectFiles()
{
my $onestr, @lines;
my @mmp_files_plus_one = @mmp_files;
-# unshift @mmp_files_plus_one, "mmp/scummvm_base.mmp";
-
+# unshift @mmp_files_plus_one, "mmp/scummvm_base.mmp";
+
print "Resetting project files: ";
-
+
# we don't need to do mmp/scummvm_base.mmp", it was done in BuildPackageUpload.pl before the call to this script
foreach $name (@mmp_files_plus_one)
{
@@ -471,7 +473,7 @@ sub ResetProjectFiles()
$onestr = join("",@lines);
open FILE, ">$fileBLDINF"; print FILE $onestr; close FILE;
}
- }
+ }
print "... done.\n";
}
diff --git a/backends/platform/symbian/BuildPackageUpload_AllVersions.pl b/backends/platform/symbian/BuildPackageUpload_AllVersions.pl
index 62fcef0275..3bdcede76a 100644
--- a/backends/platform/symbian/BuildPackageUpload_AllVersions.pl
+++ b/backends/platform/symbian/BuildPackageUpload_AllVersions.pl
@@ -17,7 +17,7 @@ $SDK_BuildDirs{'S60v3'} = "S60v3";
$SDK_BuildDirs{'S80'} = "S80";
$SDK_BuildDirs{'S90'} = "S90";
-# the target name inserted here: 'abld BUILD $SDK_TargetName UREL'
+# the target name inserted here: 'abld BUILD $SDK_TargetName UREL'
$SDK_TargetName{'UIQ2'} = "armi";
$SDK_TargetName{'UIQ3'} = "gcce";
$SDK_TargetName{'S60v1'}= "armi";
@@ -117,7 +117,7 @@ foreach $Library (sort keys(%PresentLibs))
# push @Packages, sprintf($file_tpl_sis, $version_tpl_sis, $SDK2, $Extra);
# $PackagesQueued++;
# }
-# }
+# }
# }
# else
# {
@@ -144,7 +144,7 @@ while( ($SDK, $Value) = each(%VariationSets) )
push @Packages, sprintf($file_tpl_sis, $version_tpl_sis, $SDK2, $Extra);
$PackagesQueued++;
}
- }
+ }
}
else
{
@@ -170,7 +170,7 @@ Preparing to Build, Package & Upload $PackagesQueued SymbianOS ScummVM variation
SDKs inst'd \t$SDKs ".( %SDK_LibraryDirs ? "
LIBs inst'd \t$LIBs " : "" )."
- $PackagesQueued Variations \t$PackagesStr
+ $PackagesQueued Variations \t$PackagesStr
DIR base \t$base_dir
build \t$build_dir
output \t$output_dir
@@ -178,7 +178,7 @@ Preparing to Build, Package & Upload $PackagesQueued SymbianOS ScummVM variation
FTP host \t$FTP_Host
user \t$FTP_User
pass \t"."*" x length($FTP_Pass)."
- dir \t$FTP_Dir
+ dir \t$FTP_Dir
" : "" )."
=======================================================================================
Press Ctrl-C to abort or enter to continue Build, Package & Upload $PackagesQueued Variations...
@@ -194,7 +194,7 @@ unlink($build_log_out);
unlink($build_log_err);
# init _base.mmp now, so we can start changing it without affecting the CVS version _base.mmp.in!
-my $name = "mmp/scummvm_base.mmp";
+my $name = "mmp/scummvm_base.mmp";
my $file = "$build_dir/$name";
open FILE, "$file.in"; @lines = <FILE>; close FILE;
my $onestr = join("",@lines);
@@ -219,7 +219,7 @@ while( ($SDK, $Value) = each(%SDK_LibraryDirs) )
$LibrariesQueued++;
DoLibrary($SDK2, $Library, $Path);
}
- }
+ }
}
else
{
@@ -252,7 +252,7 @@ while( ($SDK, $VariationsHash) = each(%SDK_Variations) )
{
DoVariation($SDK2, $Variation, $MacroBlock);
}
- }
+ }
}
else
{
@@ -277,7 +277,7 @@ while( ($SDK, $VariationsHash) = each(%VariationSets) )
{
DoVariation($SDK2, $Variation, $MacroBlock);
}
- }
+ }
}
else
{
@@ -335,7 +335,7 @@ print " SumthinWicked wishes you a ridiculously good and optimally happy d
sub MakeMppMacroDefs
{
my ($features) = @_;
-
+
my %EnabledFeatures = ();
foreach (split(/\W|\r|\n/, $features))
{
@@ -398,14 +398,14 @@ sub MakeMppMacroDefs
$MacroDefs .= "//MACRO ENABLE_$E\n";
}
}
-
+
#print "\n\n'$features' ==> $MacroDefs\n\n\n";
return $MacroDefs;
}
##################################################################################################################
-# Build, Package & Upload a single Variation
+# Build, Package & Upload a single Variation
sub DoLibrary
{
my ($SDK, $Library, $Path) = @_;
@@ -435,23 +435,23 @@ my $header = "
my $OK = 1;
PrepSdkPaths($SDK);
-
+
chdir($Path) or $OK=0;
PrintErrorMessage("Changing to $Path failed!") if (!$OK);
- return 0 if (!$OK);
+ return 0 if (!$OK);
PrintMessage("Cleaning for $Target") if (!$ReallyQuiet);
system("bldmake bldfiles > NUL 2> NUL");
PrintErrorMessage("'bldmake bldfiles' exited with value " . ($? >> 8)) if ($? >> 8);
system("abld MAKEFILE $TargetName > NUL 2> NUL");
- PrintErrorMessage("'abld MAKEFILE $TargetName' exited with value " . ($? >> 8)) if ($? >> 8);
+ PrintErrorMessage("'abld MAKEFILE $TargetName' exited with value " . ($? >> 8)) if ($? >> 8);
system("abld CLEAN $TargetName UREL > NUL 2> NUL");
- PrintErrorMessage("'abld CLEAN $TargetName urel' exited with value " . ($? >> 8)) if ($? >> 8);
+ PrintErrorMessage("'abld CLEAN $TargetName urel' exited with value " . ($? >> 8)) if ($? >> 8);
# remove file so we are sure that after .lib generation we have a fresh copy!
if (-e $TargetFilePath) { unlink($TargetFilePath) or PrintErrorMessage("Removing $TargetFilePath"); }
-
+
my $Redirection = "OUT:file, ERR:".($RedirectSTDERR ? "file" : "screen");
my $Message = "Building $Target ($Redirection)";
PrintMessage($Message) if (!$ReallyQuiet);
@@ -463,14 +463,14 @@ my $header = "
$OK = 0 if ($? >> 8);
# print " STDERR: ".((-s $build_log_err)-$OldSize)." bytes output written to $build_log_err\n+--------------------------------------------------------------------------------------\n" if ($OldSize != (-s $build_log_err));
PrintErrorMessage("'abld TARGET $TargetName UREL' exited with value " . ($? >> 8)) if ($? >> 8);
- return 0 if (!$OK); # ABLD always returns ok :( grr
+ return 0 if (!$OK); # ABLD always returns ok :( grr
PrintMessage("Done.") if (!$ReallyQuiet);
# did it work? :)
if (-e $TargetFilePath)
{
$LibrariesSucceeded++;
-
+
if ($TargetIntermediatePath ne '' && $TargetIntermediatePath =~ /\\EPOC32\\BUILD\\/i) # make really sure it's a valid path!
{
system("del /S /Q $TargetIntermediatePath > NUL");
@@ -491,12 +491,12 @@ my $header = "
##################################################################################################################
-# Build, Package & Upload a single Variation
+# Build, Package & Upload a single Variation
sub DoVariation
{
my ($SDK, $Variation, $MacroBlock) = @_;
my $Extra = ($Variation ne '' ? "_$Variation" : "");
- my $Package = sprintf($file_tpl_sis, $version_tpl_sis, $SDK, $Extra);
+ my $Package = sprintf($file_tpl_sis, $version_tpl_sis, $SDK, $Extra);
if ($SkipExistingPackages && -f "$output_dir/$Package")
{
@@ -527,7 +527,7 @@ my $header = "
if ($OK)
{
$OK = BuildVariation($SDK, $Variation, $Package, $MacroBlock);
-
+
if ($OK && $FTP_Host ne '')
{
UploadVariation($SDK, $Variation, $Package);
@@ -543,17 +543,17 @@ sub PrepVariation()
my $OK = 1;
PrepSdkPaths($SDK);
-
+
chdir($build_dir) or $OK=0;
PrintErrorMessage("Changing to $build_dir failed!") if (!$OK);
- return 0 if (!$OK);
+ return 0 if (!$OK);
# insert $MacroBlock into AUTO_MACRO_MASTER in scummvm_base.mmp
PrintMessage("Setting new AUTO_MACROS_MASTER in scummvm_base.mmp for '$Variation'") if (!$ReallyQuiet);
my $n = "AUTO_MACROS_MASTER";
my $a = "\/\/START_$n\/\/";
my $b = "\/\/STOP_$n\/\/";
- my $name = "scummvm_base.mmp";
+ my $name = "scummvm_base.mmp";
my $file = "$build_dir/mmp/$name";
my $updated = " Updated @ ".localtime();
@@ -562,11 +562,11 @@ sub PrepVariation()
return 0 if (!$OK);
my @lines = <FILE>;
close FILE;
-
+
my $onestr = join("",@lines);
$MacroBlock =~ s/^\s*//gm;
$onestr =~ s/$a(.*)$b/$a$updated\n$ExtraMacros$MacroBlock$b/s;
-
+
open FILE, ">$file" or $OK=0;
PrintErrorMessage("Writing file '$file'") if (!$OK);
return 0 if (!$OK);
@@ -579,7 +579,7 @@ sub PrepVariation()
$OK = 0 if ($? >> 8);
PrintErrorMessage("'AdaptAllMMPs.pl' exited with value " . ($? >> 8)) if ($? >> 8);
return 0 if (!$OK);
-
+
# we are here: so all is ok :)
return 1;
}
@@ -592,7 +592,7 @@ sub BuildVariation()
my $TargetName = $SDK_TargetName{$SDK};
my $TargetDir = $SDK_TargetDir{$SDK};
my $OK = 1;
-
+
my $dir = $build_dir."/".$SDK_BuildDirs{$SDK};
$dir =~ s#/#\\#g;
chdir($dir);
@@ -617,8 +617,8 @@ sub BuildVariation()
PrintErrorMessage("'bldmake bldfiles' exited with value " . ($? >> 8)) if ($? >> 8);
system("abld CLEAN $TargetName UREL 2> NUL > NUL");
- PrintErrorMessage("'abld CLEAN $TargetName UREL' exited with value " . ($? >> 8)) if ($? >> 8);
-
+ PrintErrorMessage("'abld CLEAN $TargetName UREL' exited with value " . ($? >> 8)) if ($? >> 8);
+
my $Redirection = "OUT:file, ERR:".($RedirectSTDERR ? "file" : "screen");
my $Message = "Building $Package ($Redirection)";
PrintMessage($Message) if (!$ReallyQuiet);
@@ -630,7 +630,7 @@ sub BuildVariation()
$OK = 0 if ($? >> 8);
print " STDERR: ".((-s $build_log_err)-$OldSize)." bytes output written to $build_log_err\n+--------------------------------------------------------------------------------------\n" if ($OldSize != (-s $build_log_err) && !$ReallyQuiet);
PrintErrorMessage("'abld BUILD $TargetName UREL' exited with value " . ($? >> 8)) if ($? >> 8);
- return 0 if (!$OK); # ABLD always returns ok :( grr
+ return 0 if (!$OK); # ABLD always returns ok :( grr
PrintMessage("Done.") if (!$ReallyQuiet);
# do we have an override suffix for the package name?
@@ -654,7 +654,7 @@ sub BuildVariation()
if (-e "$output_dir/$Package")
{
$PackagesCreated++;
-
+
if ($TargetIntermediatePath ne '' && $TargetIntermediatePath =~ /\\EPOC32\\BUILD\\/i) # make really sure it's a valid path!
{
#PrintMessage("Cleaning $TargetIntermediatePath");
@@ -677,7 +677,7 @@ sub UploadVariation()
use Net::FTP;
my $newerr;
-
+
PrintMessage("Connecting to FTP $FTP_Host") if (!$ReallyQuiet);
$ftp = Net::FTP->new($FTP_Host,Timeout=>240) or $newerr=1;
@@ -692,7 +692,7 @@ sub UploadVariation()
{
PrintMessage("Changing to dir $FTP_Dir");
$ftp->cwd($FTP_Dir) or $newerr=1;
-
+
if ($newerr)
{
PrintErrorMessage("Changing to dir $FTP_Dir! Aborting!");
@@ -704,20 +704,20 @@ sub UploadVariation()
# leave this for possible auto-deletion of old files?
# @files = $ftp->dir or $newerr=1;
# push @ERRORS, "Can't get file list $!\n" if $newerr;
-# print "Got file list\n";
+# print "Got file list\n";
# foreach(@files) {
# print "$_\n";
# }
-
+
PrintMessage("Uploading $Package (".(-s "$output_dir/$Package")." bytes)");
-
+
$ftp->binary;
$ftp->put("$output_dir/$Package") or $newerr=1;
PrintErrorMessage("Uploading package! Aborting!") if $newerr;
$PackagesUploaded++ if (!$newerr);
- }
+ }
- $ftp->quit;
+ $ftp->quit;
}
}
@@ -758,7 +758,7 @@ sub CleanupPath()
{
$path =~ s/\"\Q$ECompXL_BinDir\E\";//g;
}
-
+
while( ($SDK, $RootDir) = each(%SDK_RootDirs) )
{
if ($SDK_RootDirs{$SDK} ne '')
@@ -766,8 +766,8 @@ sub CleanupPath()
my $path_component = "\"".$SDK_RootDirs{$SDK}."\\epoc32\\";
$path =~ s/\Q$path_component\E.*?\";//g;
}
- }
-
+ }
+
return $path;
}
@@ -798,5 +798,5 @@ sub PrintMessage()
}
##################################################################################################################
-
+
diff --git a/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl b/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl
index b6ebbafe74..bf80c36a0e 100644
--- a/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl
+++ b/backends/platform/symbian/BuildPackageUpload_LocalSettings.pl
@@ -2,26 +2,26 @@
##################################################################################################################
@WorkingEngines = qw(
- scumm agos sky queen gob groovie saga drascula
+ scumm agos sky queen gob groovie saga drascula
kyra lure agi touche parallaction cine
- cruise made m4 tinsel tucker sword1 sword2 draci sci teenagent mohawk hugo
+ cruise made m4 tinsel tucker sword1 sword2 draci sci teenagent mohawk hugo toon
);
-
+
@WorkingEngines_1st = qw(
- scumm queen groovie saga drascula
+ scumm queen groovie saga drascula
touche parallaction cine
-? cruise made m4 tucker
+? cruise made m4 tucker
);
-
+
@WorkingEngines_2nd = qw(
- agos sky gob
+ agos sky gob
kyra lure agi tinsel
- sword1 sword2 draci sci teenagent hugo
+ sword1 sword2 draci sci teenagent hugo toon
);
@TestingEngines = qw(
-
- );
+
+ );
@EnablableEngines = (@WorkingEngines, @TestingEngines);
@@ -36,16 +36,16 @@
#disabled subengines lol saga2 personal nightmare
%UseableFeatures = (
- 'zlib' => 'zlib.lib',
- 'mad' => 'libmad.lib',
+ 'zlib' => 'zlib.lib',
+ 'mad' => 'libmad.lib',
'tremor' => 'libtremor.lib',
'flac' => 'libflac.lib'
);
-
+
# these are normally enabled for each variation
#$DefaultFeatures = qw(zlib,mad);
- $DefaultFeatures = qw(zlib,mad,tremor,flac);
-
+ $DefaultFeatures = qw(zlib,mad,tremor,flac);
+
##################################################################################################################
##
## General system information, based on $COMPUTERNAME, so this way
@@ -62,13 +62,13 @@
$SkipExistingPackages = 0;
$ReallyQuiet = 0;
$DevBase = "C:\\S";
-
+
# specify an optional FTP server to upload to after each Build+Package (can leave empty)
#$FTP_Host = "host.com";
$FTP_User = "something";
$FTP_Pass = "password";
$FTP_Dir = "cvsbuilds";
-
+
# What Platform SDKs are installed on this machine?
# possible SDKs: ("UIQ2", UIQ3", "S60v1", "S60v2", "S60v3", "S80", "S90")
# Note1: the \epoc32 directory needs to be in these rootdirs
@@ -93,10 +93,10 @@
$SDK_LibraryDirs{'ALL'}{'zlib.lib'} = "$DevBase\\zlib-1.2.2\\epoc";
#$SDK_LibraryDirs{'ALL'}{'libmad.lib'} = "$DevBase\\libmad-0.15.1b\\group";
$SDK_LibraryDirs{'ALL'}{'libtremor.lib'}= "$DevBase\\tremor\\epoc";
-
+
## SDL 1.2.12 / AnotherGuest / Symbian version
my $SdlBase = "$DevBase\\SDL-1.2.12-ag\\Symbian";
- #$SDK_LibraryDirs{'S60v1'}{'esdl.lib'} = "$SdlBase\\S60"; // unsupported?
+ #$SDK_LibraryDirs{'S60v1'}{'esdl.lib'} = "$SdlBase\\S60"; // unsupported?
#$SDK_LibraryDirs{'S60v2'}{'esdl.lib'} = "$SdlBase\\S60v2";
$SDK_LibraryDirs{'S60v3'}{'esdl.lib'} = "$SdlBase\\S60v3";
#$SDK_LibraryDirs{'S80'}{'esdl.lib'} = "$SdlBase\\S80";
@@ -107,7 +107,7 @@
## HardlySupported(TM) :P
#$SDK_LibraryDirs{'ALL'}{'libmpeg2.lib'} = "$DevBase\\mpeg2dec-0.4.0\\epoc";
}
-
+
# now you can add $VariationSets only built on this PC below this line :)
#$VariationSets{'ALL'}{'scumm'} = "$DefaultFeatures scumm scumm_7_8 he";
@@ -296,14 +296,14 @@
# scummvm-051101-SymbianS90_queen.sis
# NOTE: empty $VariationSets{''} string instead of 'ALL' = easy way to disable pkg!
-
+
if (1) # all regular combo's
{
# the first one includes all SDKs & release-ready engines
-
+
$VariationSets{'ALL'}{'all'} = "$DefaultFeatures @WorkingEngines @EnablableSubEngines";
- $VariationSets{'ALL'}{'1St'} = "$DefaultFeatures @WorkingEngines_1st @EnablableSubEngines";
- $VariationSets{'ALL'}{'2nd'} = "$DefaultFeatures @WorkingEngines_2nd @EnablableSubEngines";
+# $VariationSets{'ALL'}{'1St'} = "$DefaultFeatures @WorkingEngines_1st @EnablableSubEngines";
+# $VariationSets{'ALL'}{'2nd'} = "$DefaultFeatures @WorkingEngines_2nd @EnablableSubEngines";
# now one for each ready-for-release engine
if (0)
{
@@ -324,16 +324,16 @@
}
}
# below here you could specify weird & experimental combinations, non-ready engines
-
+
# Separate version for the broken sword engines (1&2)
#$VariationSets{'ALL'}{'brokensword'} = "$DefaultFeatures sword1 sword2";
-
+
# Separate version for Scumm games (COMI) since memory usage might be high
- #$VariationSets{'ALL'}{'scumm'} = "$DefaultFeatures scumm scumm_7_8 he";
-
+ #$VariationSets{'ALL'}{'scumm'} = "$DefaultFeatures scumm scumm_7_8 he";
+
# for mega-fast-testing only plz! Warning: contains to engines!
#$VariationSets{'ALL'}{'fast_empty'} = "";
-
+
} # end quick-n-fast if (1|0)
diff --git a/backends/platform/symbian/README b/backends/platform/symbian/README
index 630b539a5f..ba7099ba32 100644
--- a/backends/platform/symbian/README
+++ b/backends/platform/symbian/README
@@ -24,7 +24,7 @@ About ScummVM
The original ports (uptil 0.7.1) were made by Andreas Karlsson and Lars Persson.
The main transition to 0.8.0CVS and all relevant changes were done by Jurgen Braam.
Jurgen and Lars have successfully transfered all needed changes into CVS/SVN, with additional helpful tools for Symbian OS
-
+
Release History:
Release version: 1.1.0
* Nothing significant in the Symbian port, except SDL improvements.
@@ -33,7 +33,7 @@ Release History:
Release version: 1.0.0
* Nothing significant in the Symbian port, except SDL improvements.
* See main readme for general ScummVM improvements, minor update
-
+
Release version: 0.13.1
* Nothing significant in the Symbian port, except SDL improvements.
* See main readme for general ScummVM improvements, minor update
diff --git a/backends/platform/symbian/S60/BLD.INF.in b/backends/platform/symbian/S60/BLD.INF.in
index b6cf8bbda6..c3c3decf1a 100644
--- a/backends/platform/symbian/S60/BLD.INF.in
+++ b/backends/platform/symbian/S60/BLD.INF.in
@@ -5,7 +5,7 @@ PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
.\ScummVM_S60.mmp
diff --git a/backends/platform/symbian/S60v3/BLD.INF.in b/backends/platform/symbian/S60v3/BLD.INF.in
index 10f46ced61..78d130cb08 100644
--- a/backends/platform/symbian/S60v3/BLD.INF.in
+++ b/backends/platform/symbian/S60v3/BLD.INF.in
@@ -5,7 +5,7 @@ PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
gnumakefile icons.mk
.\ScummVM_A0000658_S60v3.mmp
diff --git a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
index 2916d73938..d044b33bb0 100644
--- a/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
+++ b/backends/platform/symbian/S60v3/ScummVM_A0000658_S60v3.mmp.in
@@ -97,6 +97,9 @@ SYSTEMINCLUDE \epoc32\include\ESDL
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include\libc
SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\mw
+SYSTEMINCLUDE \epoc32\include\platform
+SYSTEMINCLUDE \epoc32\include\platform\mw
SYSTEMINCLUDE ..\src // for portdefs.h
// *** SOURCE files
@@ -126,7 +129,7 @@ SOURCE gui\KeysDialog.cpp
SOURCE gui\Actions.cpp
SOURCE gui\Dialog.cpp
-// Common error
+// Common error
source common\error.cpp
// Special for graphics
@@ -140,4 +143,4 @@ LIBRARY gdi.lib hal.lib bitgdi.lib
LIBRARY mediaclientaudiostream.lib efsrv.lib ws32.lib
library avkon.lib bafl.lib
-CAPABILITY LocalServices ReadUserData \ No newline at end of file
+CAPABILITY LocalServices ReadUserData
diff --git a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
index e95bb9ee21..bcc00017ab 100644
--- a/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
+++ b/backends/platform/symbian/S60v3/ScummVM_S60v3.mmp.in
@@ -97,6 +97,9 @@ SYSTEMINCLUDE \epoc32\include\ESDL
SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
SYSTEMINCLUDE \epoc32\include\libc
SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\mw
+SYSTEMINCLUDE \epoc32\include\platform
+SYSTEMINCLUDE \epoc32\include\platform\mw
SYSTEMINCLUDE ..\src // for portdefs.h
// *** SOURCE files
@@ -126,7 +129,7 @@ SOURCE gui\KeysDialog.cpp
SOURCE gui\Actions.cpp
SOURCE gui\Dialog.cpp
-// Common error
+// Common error
source common\error.cpp
// Special for graphics
@@ -140,4 +143,4 @@ LIBRARY gdi.lib hal.lib bitgdi.lib
LIBRARY mediaclientaudiostream.lib efsrv.lib ws32.lib
library avkon.lib bafl.lib
-CAPABILITY LocalServices ReadUserData \ No newline at end of file
+CAPABILITY LocalServices ReadUserData
diff --git a/backends/platform/symbian/S60v3/scummvm-CVS-SymbianS60v3.pkg b/backends/platform/symbian/S60v3/scummvm-CVS-SymbianS60v3.pkg
index a2c43d94e6..3f0fbbc5f1 100644
--- a/backends/platform/symbian/S60v3/scummvm-CVS-SymbianS60v3.pkg
+++ b/backends/platform/symbian/S60v3/scummvm-CVS-SymbianS60v3.pkg
@@ -36,7 +36,7 @@
:"ScummVM"
; UID is the app's UID
-#{"ScummVM S60v3"},(0xA0000657),1,12,0
+#{"ScummVM S60v3"},(0xA0000657),1,13,0
;Supports Series 60 v 3.0
[0x101F7961], 0, 0, 0, {"Series60ProductID"}
@@ -68,6 +68,7 @@
"..\..\..\..\dists\engine-data\m4.dat"-"c:\data\scummvm\m4.dat"
"..\..\..\..\dists\engine-data\teenagent.dat"-"c:\data\scummvm\teenagent.dat"
"..\..\..\vkeybd\packs\vkeybd_default.zip"-"c:\data\scummvm\vkeybd_default.zip"
+"..\..\..\..\gui\themes\translations.dat"-"c:\data\scummvm\translations.dat"
; Config/log files: 'empty' will automagically be removed on uninstall
""-"c:\data\scummvm\scummvm.ini",FILENULL
diff --git a/backends/platform/symbian/S80/BLD.INF.in b/backends/platform/symbian/S80/BLD.INF.in
index 75ef20be12..c25ee43a99 100644
--- a/backends/platform/symbian/S80/BLD.INF.in
+++ b/backends/platform/symbian/S80/BLD.INF.in
@@ -5,7 +5,7 @@ PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
.\ScummVM_S80.mmp
diff --git a/backends/platform/symbian/S90/BLD.INF.in b/backends/platform/symbian/S90/BLD.INF.in
index d6e1ece012..4e333e5dae 100644
--- a/backends/platform/symbian/S90/BLD.INF.in
+++ b/backends/platform/symbian/S90/BLD.INF.in
@@ -5,7 +5,7 @@ PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
.\ScummVM_S90.mmp
diff --git a/backends/platform/symbian/UIQ2/BLD.INF.in b/backends/platform/symbian/UIQ2/BLD.INF.in
index b607446a64..b8d8111e87 100644
--- a/backends/platform/symbian/UIQ2/BLD.INF.in
+++ b/backends/platform/symbian/UIQ2/BLD.INF.in
@@ -5,7 +5,7 @@ PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
.\ScummVM_UIQ2.mmp
diff --git a/backends/platform/symbian/UIQ3/BLD.INF.in b/backends/platform/symbian/UIQ3/BLD.INF.in
index e4b0bb306e..ff12dbdd75 100644
--- a/backends/platform/symbian/UIQ3/BLD.INF.in
+++ b/backends/platform/symbian/UIQ3/BLD.INF.in
@@ -1,11 +1,11 @@
PRJ_PLATFORMS
-GCCE WINSCW
+GCCE WINSCW
PRJ_MMPFILES
//START_AUTO_PROJECTS//
// empty base file, will be updated by Perl build scripts
-
+
//STOP_AUTO_PROJECTS//
.\ScummVM_A0000658_UIQ3.mmp
.\ScummVM_UIQ3.mmp
diff --git a/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in b/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in
index 99cbb264c2..83ce9bc599 100644
--- a/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in
+++ b/backends/platform/symbian/UIQ3/ScummVM_A0000658_UIQ3.mmp.in
@@ -127,7 +127,7 @@ SOURCE gui\KeysDialog.cpp
SOURCE gui\Actions.cpp
SOURCE gui\Dialog.cpp
-// Common error
+// Common error
source common\error.cpp
// Special for graphics
diff --git a/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in b/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in
index 3509714152..3c8e41784a 100644
--- a/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in
+++ b/backends/platform/symbian/UIQ3/ScummVM_UIQ3.mmp.in
@@ -127,7 +127,7 @@ SOURCE gui\KeysDialog.cpp
SOURCE gui\Actions.cpp
SOURCE gui\Dialog.cpp
-// Common error
+// Common error
source common\error.cpp
// Special for graphics
diff --git a/backends/platform/symbian/UIQ3/scummvm-CVS-SymbianUIQ3.pkg b/backends/platform/symbian/UIQ3/scummvm-CVS-SymbianUIQ3.pkg
index 7e620485d5..a55f4b6e37 100644
--- a/backends/platform/symbian/UIQ3/scummvm-CVS-SymbianUIQ3.pkg
+++ b/backends/platform/symbian/UIQ3/scummvm-CVS-SymbianUIQ3.pkg
@@ -35,7 +35,7 @@
:"ScummVM"
; UID is the app's UID
-#{"ScummVM UIQ3"},(0xA0000657),1,12,0
+#{"ScummVM UIQ3"},(0xA0000657),1,13,0
; ProductID for UIQ 3.0
; Product/platform version UID, Major, Minor, Build, Product ID
@@ -65,6 +65,8 @@
"..\..\..\..\dists\engine-data\m4.dat"-"c:\shared\scummvm\m4.dat"
"..\..\..\..\dists\engine-data\teenagent.dat"-"c:\shared\scummvm\teenagent.dat"
"..\..\..\vkeybd\packs\vkeybd_default.zip"-"c:\shared\scummvm\vkeybd_default.zip"
+"..\..\..\..\gui\themes\translations.dat"-"c:\shared\scummvm\translations.dat"
+
; Config/log files: 'empty' will automagically be removed on uninstall
""-"c:\shared\scummvm\scummvm.ini",FILENULL
""-"c:\shared\scummvm\scummvm.stdout.txt",FILENULL
diff --git a/backends/platform/symbian/mmp/scummvm_base.mmp.in b/backends/platform/symbian/mmp/scummvm_base.mmp.in
index ceb62ec843..8f51e3c7b5 100644
--- a/backends/platform/symbian/mmp/scummvm_base.mmp.in
+++ b/backends/platform/symbian/mmp/scummvm_base.mmp.in
@@ -59,6 +59,9 @@ SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian alr
SYSTEMINCLUDE \epoc32\include\libc
SYSTEMINCLUDE \epoc32\include\tremor
SYSTEMINCLUDE \epoc32\include
+SYSTEMINCLUDE \epoc32\include\mw
+SYSTEMINCLUDE \epoc32\include\platform
+SYSTEMINCLUDE \epoc32\include\platform\mw
SYSTEMINCLUDE ..\src // for portdefs.h
// *** SOURCE files
diff --git a/backends/platform/symbian/mmp/scummvm_toon.mmp.in b/backends/platform/symbian/mmp/scummvm_toon.mmp.in
new file mode 100644
index 0000000000..7b907fd92f
--- /dev/null
+++ b/backends/platform/symbian/mmp/scummvm_toon.mmp.in
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ * Copyright (C) 2003-2005 Andreas 'Sprawl' Karlsson - Original EPOC port, ESDL
+ * Copyright (C) 2003-2005 Lars 'AnotherGuest' Persson - Original EPOC port, Audio System
+ * Copyright (C) 2005 Jurgen 'SumthinWicked' Braam - EPOC/CVS maintainer
+ * Copyright (C) 2005-2010 The ScummVM project
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+//
+// EPOC MMP makefile project for ScummVM
+//
+
+// *** Definitions
+
+TARGET scummvm_toon.lib
+TARGETTYPE lib
+OPTION MSVC /QIfist /Ob1 /Oy /GF // /QIfist disables use of __ftol2 to avoid linker probs with MS libc: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/vcrefQIfistSuppress_ftol.asp
+OPTION GCC -Wno-multichar -Wno-reorder // don't optimize for ARM, platform way too sensitive for that :( just turn off some common warnings
+OPTION GCCE -Wno-multichar -Wno-reorder -Wno-unused -Wno-format -fsigned-char
+ALWAYS_BUILD_AS_ARM
+
+//START_AUTO_MACROS_SLAVE//
+
+ // empty base file, will be updated by Perl build scripts
+
+//STOP_AUTO_MACROS_SLAVE//
+
+// *** SOURCE files
+
+SOURCEPATH ..\..\..\..\engines\toon
+
+//START_AUTO_OBJECTS_TOON_//
+
+ // empty base file, will be updated by Perl build scripts
+
+//STOP_AUTO_OBJECTS_TOON_//
+
+// *** Include paths
+
+USERINCLUDE ..\..\..\..\engines
+USERINCLUDE ..\..\..\.. ..\..\..\..\common ..\..\..\..\gui ..\..\..\..\sound ..\src
+SYSTEMINCLUDE \epoc32\include\ZLIB // before \epoc32\include because symbian already has older version
+SYSTEMINCLUDE \epoc32\include \epoc32\include\libc ..\src
diff --git a/backends/platform/symbian/res/scummvm.rss b/backends/platform/symbian/res/scummvm.rss
index 0bb5dff058..f7daf9bfc1 100644
--- a/backends/platform/symbian/res/scummvm.rss
+++ b/backends/platform/symbian/res/scummvm.rss
@@ -35,7 +35,6 @@ NAME SCUM
#include <eikon.rh>
#include "..\src\Scummvm.hrh"
// Include the standard Eikon resource ids
-#include <eikon.rsg>
RESOURCE RSS_SIGNATURE
diff --git a/backends/platform/symbian/src/ScummApp.cpp b/backends/platform/symbian/src/ScummApp.cpp
index a3ce692368..01af5e7fd9 100644
--- a/backends/platform/symbian/src/ScummApp.cpp
+++ b/backends/platform/symbian/src/ScummApp.cpp
@@ -27,7 +27,7 @@
#define _PAGESIZE_ 0x1000
-#if defined (__WINS__) && !defined (__SERIES60_30__) && !defined (UIQ3)
+#if defined (__WINS__) && !defined (S60V3) && !defined (UIQ3)
extern "C" int _chkstk(int /*a*/) {
_asm {
push ecx
diff --git a/backends/platform/symbian/src/ScummApp.h b/backends/platform/symbian/src/ScummApp.h
index 8cfdbdb92f..cca8e2a7c9 100644
--- a/backends/platform/symbian/src/ScummApp.h
+++ b/backends/platform/symbian/src/ScummApp.h
@@ -22,8 +22,8 @@
* $Id$
*/
-#ifndef ScummAPPH
-#define ScummAPPH
+#ifndef SCUMMAPP_H
+#define SCUMMAPP_H
#include <eikapp.h>
#include <e32base.h>
diff --git a/backends/platform/symbian/src/ScummVMApp.h b/backends/platform/symbian/src/ScummVMApp.h
index 6796024e1e..aee098f989 100644
--- a/backends/platform/symbian/src/ScummVMApp.h
+++ b/backends/platform/symbian/src/ScummVMApp.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef ScummVMapph
-#define ScummVMapph
+#ifndef SCUMMVMAPP_H
+#define SCUMMVMAPP_H
#include <eikapp.h>
#include <e32base.h>
diff --git a/backends/platform/symbian/src/SymbianOS.cpp b/backends/platform/symbian/src/SymbianOS.cpp
index d63c9fe044..50ab7e00b1 100644
--- a/backends/platform/symbian/src/SymbianOS.cpp
+++ b/backends/platform/symbian/src/SymbianOS.cpp
@@ -140,9 +140,6 @@ OSystem_SDL_Symbian::OSystem_SDL_Symbian() :_channels(0),_stereo_mix_buffer(0) {
}
void OSystem_SDL_Symbian::initBackend() {
- // First set the extrapath (for installed dat files etc)
- ConfMan.set("extrapath", Symbian::GetExecutablePath());
-
// Calculate the default savepath
Common::String savePath;
savePath = Symbian::GetExecutablePath();
@@ -183,6 +180,13 @@ void OSystem_SDL_Symbian::initBackend() {
initZones();
}
+void OSystem_SDL_Symbian::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+ Common::FSNode pluginsNode(Symbian::GetExecutablePath());
+ if (pluginsNode.exists() && pluginsNode.isDirectory()) {
+ s.add("SYMBIAN_DATAFOLDER", new Common::FSDirectory(Symbian::GetExecutablePath()), priority);
+ }
+}
+
OSystem_SDL_Symbian::~OSystem_SDL_Symbian() {
delete[] _stereo_mix_buffer;
}
diff --git a/backends/platform/symbian/src/SymbianOS.h b/backends/platform/symbian/src/SymbianOS.h
index 42929f8029..85b5e131da 100644
--- a/backends/platform/symbian/src/SymbianOS.h
+++ b/backends/platform/symbian/src/SymbianOS.h
@@ -22,8 +22,8 @@
* $Id$
*/
-#ifndef SDLSYMBIANH
-#define SDLSYMBIANH
+#ifndef SDLSYMBIAN_H
+#define SDLSYMBIAN_H
#include "backends/platform/sdl/sdl.h"
@@ -61,6 +61,8 @@ public:
// Returns reference to File session
RFs& FsSession();
+
+ void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
protected:
//
// The mixer callback function.
diff --git a/backends/platform/wii/options.cpp b/backends/platform/wii/options.cpp
index 0a21b45561..2a47958e3b 100644
--- a/backends/platform/wii/options.cpp
+++ b/backends/platform/wii/options.cpp
@@ -183,7 +183,7 @@ void WiiOptionsDialog::handleTickle() {
break;
default:
- label = String::printf(_("Network not initialsed (%d)"), status);
+ label = String::printf(_("Network not initialised (%d)"), status);
break;
}
diff --git a/backends/platform/wince/CEActionsPocket.h b/backends/platform/wince/CEActionsPocket.h
index ae0ed19975..0fe29a9e86 100644
--- a/backends/platform/wince/CEActionsPocket.h
+++ b/backends/platform/wince/CEActionsPocket.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEACTIONSPOCKET
-#define CEACTIONSPOCKET
+#ifndef CEACTIONSPOCKET_H
+#define CEACTIONSPOCKET_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEActionsSmartphone.h b/backends/platform/wince/CEActionsSmartphone.h
index eacc571a67..36797cd2c8 100644
--- a/backends/platform/wince/CEActionsSmartphone.h
+++ b/backends/platform/wince/CEActionsSmartphone.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEACTIONSSMARTPHONE
-#define CEACTIONSSMARTPHONE
+#ifndef CEACTIONSSMARTPHONE_H
+#define CEACTIONSSMARTPHONE_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEDevice.h b/backends/platform/wince/CEDevice.h
index 9fee8497c2..ca2f908c6d 100644
--- a/backends/platform/wince/CEDevice.h
+++ b/backends/platform/wince/CEDevice.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEDEVICE
-#define CEDEVICE
+#ifndef CEDEVICE_H
+#define CEDEVICE_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CELauncherDialog.h b/backends/platform/wince/CELauncherDialog.h
index 7e86f808fc..7755e33dcc 100644
--- a/backends/platform/wince/CELauncherDialog.h
+++ b/backends/platform/wince/CELauncherDialog.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CELAUNCHERDIALOG
-#define CELAUNCHERDIALOG
+#ifndef CELAUNCHERDIALOG_H
+#define CELAUNCHERDIALOG_H
#include "base/plugins.h"
#include "common/fs.h"
diff --git a/backends/platform/wince/CEScaler.h b/backends/platform/wince/CEScaler.h
index c93e1c66ca..4711f94bba 100644
--- a/backends/platform/wince/CEScaler.h
+++ b/backends/platform/wince/CEScaler.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CESCALER
-#define CESCALER
+#ifndef CESCALER_H
+#define CESCALER_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/GUIElement.h b/backends/platform/wince/CEgui/GUIElement.h
index beb9d95047..7e4572d377 100644
--- a/backends/platform/wince/CEgui/GUIElement.h
+++ b/backends/platform/wince/CEgui/GUIElement.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_GUIELEMENT
-#define CEGUI_GUIELEMENT
+#ifndef CEGUI_GUIELEMENT_H
+#define CEGUI_GUIELEMENT_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/ItemAction.h b/backends/platform/wince/CEgui/ItemAction.h
index e2cd5b2367..74ed6bec4d 100644
--- a/backends/platform/wince/CEgui/ItemAction.h
+++ b/backends/platform/wince/CEgui/ItemAction.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_ITEMACTION
-#define CEGUI_ITEMACTION
+#ifndef CEGUI_ITEMACTION_H
+#define CEGUI_ITEMACTION_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/ItemSwitch.h b/backends/platform/wince/CEgui/ItemSwitch.h
index 1fa5732b6a..8d03ee77cb 100644
--- a/backends/platform/wince/CEgui/ItemSwitch.h
+++ b/backends/platform/wince/CEgui/ItemSwitch.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_ITEMSWITCH
-#define CEGUI_ITEMSWITCH
+#ifndef CEGUI_ITEMSWITCH_H
+#define CEGUI_ITEMSWITCH_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/Panel.h b/backends/platform/wince/CEgui/Panel.h
index 0b55c00dee..6626e41866 100644
--- a/backends/platform/wince/CEgui/Panel.h
+++ b/backends/platform/wince/CEgui/Panel.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_PANEL
-#define CEGUI_PANEL
+#ifndef CEGUI_PANEL_H
+#define CEGUI_PANEL_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/PanelItem.h b/backends/platform/wince/CEgui/PanelItem.h
index 82e9b03e22..55920c304a 100644
--- a/backends/platform/wince/CEgui/PanelItem.h
+++ b/backends/platform/wince/CEgui/PanelItem.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_PANELITEM
-#define CEGUI_PANELITEM
+#ifndef CEGUI_PANELITEM_H
+#define CEGUI_PANELITEM_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/PanelKeyboard.h b/backends/platform/wince/CEgui/PanelKeyboard.h
index 5ef4adc73e..f441e14771 100644
--- a/backends/platform/wince/CEgui/PanelKeyboard.h
+++ b/backends/platform/wince/CEgui/PanelKeyboard.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_PANELKEYBOARD
-#define CEGUI_PANELKEYBOARD
+#ifndef CEGUI_PANELKEYBOARD_H
+#define CEGUI_PANELKEYBOARD_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/SDL_ImageResource.h b/backends/platform/wince/CEgui/SDL_ImageResource.h
index 140a11c356..454237dd15 100644
--- a/backends/platform/wince/CEgui/SDL_ImageResource.h
+++ b/backends/platform/wince/CEgui/SDL_ImageResource.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_SDL_IMAGERESOURCE
-#define CEGUI_SDL_IMAGERESOURCE
+#ifndef CEGUI_SDL_IMAGERESOURCE_H
+#define CEGUI_SDL_IMAGERESOURCE_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/Toolbar.h b/backends/platform/wince/CEgui/Toolbar.h
index c23559d315..3c48e6188a 100644
--- a/backends/platform/wince/CEgui/Toolbar.h
+++ b/backends/platform/wince/CEgui/Toolbar.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_TOOLBAR
-#define CEGUI_TOOLBAR
+#ifndef CEGUI_TOOLBAR_H
+#define CEGUI_TOOLBAR_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEgui/ToolbarHandler.h b/backends/platform/wince/CEgui/ToolbarHandler.h
index 30a299d153..4a79ed1609 100644
--- a/backends/platform/wince/CEgui/ToolbarHandler.h
+++ b/backends/platform/wince/CEgui/ToolbarHandler.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEGUI_TOOLBARHANDLER
-#define CEGUI_TOOLBARHANDLER
+#ifndef CEGUI_TOOLBARHANDLER_H
+#define CEGUI_TOOLBARHANDLER_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/CEkeys/EventsBuffer.h b/backends/platform/wince/CEkeys/EventsBuffer.h
index 2e13770d25..44e1c66e47 100644
--- a/backends/platform/wince/CEkeys/EventsBuffer.h
+++ b/backends/platform/wince/CEkeys/EventsBuffer.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef CEKEYS_EVENTSBUFFER
-#define CEKEYS_EVENTSBUFFER
+#ifndef CEKEYS_EVENTSBUFFER_H
+#define CEKEYS_EVENTSBUFFER_H
#include "common/scummsys.h"
#include "common/system.h"
diff --git a/backends/platform/wince/wince-sdl.cpp b/backends/platform/wince/wince-sdl.cpp
index aef2117bd7..ac9be5df48 100644
--- a/backends/platform/wince/wince-sdl.cpp
+++ b/backends/platform/wince/wince-sdl.cpp
@@ -429,7 +429,7 @@ void OSystem_WINCE3::initBackend() {
GUI::Actions::init();
GUI_Actions::Instance()->initInstanceMain(this);
if (!GUI_Actions::Instance()->loadMapping()) { // error during loading means not present/wrong version
- warning("Setting default action mappings.");
+ warning("Setting default action mappings");
GUI_Actions::Instance()->saveMapping(); // write defaults
}
@@ -987,11 +987,11 @@ void OSystem_WINCE3::check_mappings() {
// Some games need to map the right click button, signal it here if it wasn't done
if (instance->needsRightClickMapping()) {
- GUI::KeysDialog *keysDialog = new GUI::KeysDialog("Map right click action");
+ GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map right click action"));
while (!instance->getMapping(POCKET_ACTION_RIGHTCLICK)) {
keysDialog->runModal();
if (!instance->getMapping(POCKET_ACTION_RIGHTCLICK)) {
- GUI::MessageDialog alert("You must map a key to the 'Right Click' action to play this game");
+ GUI::MessageDialog alert(_("You must map a key to the 'Right Click' action to play this game"));
alert.runModal();
}
}
@@ -1000,11 +1000,11 @@ void OSystem_WINCE3::check_mappings() {
// Map the "hide toolbar" action if needed
if (instance->needsHideToolbarMapping()) {
- GUI::KeysDialog *keysDialog = new GUI::KeysDialog("Map hide toolbar action");
+ GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map hide toolbar action"));
while (!instance->getMapping(POCKET_ACTION_HIDE)) {
keysDialog->runModal();
if (!instance->getMapping(POCKET_ACTION_HIDE)) {
- GUI::MessageDialog alert("You must map a key to the 'Hide toolbar' action to play this game");
+ GUI::MessageDialog alert(_("You must map a key to the 'Hide toolbar' action to play this game"));
alert.runModal();
}
}
@@ -1013,10 +1013,10 @@ void OSystem_WINCE3::check_mappings() {
// Map the "zoom" actions if needed
if (instance->needsZoomMapping()) {
- GUI::KeysDialog *keysDialog = new GUI::KeysDialog("Map Zoom Up action (optional)");
+ GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map Zoom Up action (optional)"));
keysDialog->runModal();
delete keysDialog;
- keysDialog = new GUI::KeysDialog("Map Zoom Down action (optional)");
+ keysDialog = new GUI::KeysDialog(_("Map Zoom Down action (optional)"));
keysDialog->runModal();
delete keysDialog;
}
@@ -1024,7 +1024,7 @@ void OSystem_WINCE3::check_mappings() {
// Extra warning for Zak Mc Kracken
if (strncmp(gameid.c_str(), "zak", 3) == 0 &&
!GUI_Actions::Instance()->getMapping(POCKET_ACTION_HIDE)) {
- GUI::MessageDialog alert("Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory");
+ GUI::MessageDialog alert(_("Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"));
alert.runModal();
}
diff --git a/backends/plugins/elf/elf-loader.cpp b/backends/plugins/elf/elf-loader.cpp
index f8f1987b13..17a67f4d02 100644
--- a/backends/plugins/elf/elf-loader.cpp
+++ b/backends/plugins/elf/elf-loader.cpp
@@ -166,7 +166,7 @@ bool DLObject::loadSegment(Elf32_Phdr *phdr) {
warning("elfloader: Out of memory.");
return false;
}
-
+
debug(2, "elfloader: Allocated segment @ %p", _segment);
// Get offset to load segment into
diff --git a/backends/plugins/elf/mips-loader.cpp b/backends/plugins/elf/mips-loader.cpp
index a90e22cacd..53ffc2f01b 100644
--- a/backends/plugins/elf/mips-loader.cpp
+++ b/backends/plugins/elf/mips-loader.cpp
@@ -208,7 +208,7 @@ bool MIPSDLObject::relocate(Elf32_Off offset, Elf32_Word size, byte *relSegment)
*target = relocation;
if (debugRelocs[6]++ < DEBUG_NUM)
- debug("8, elfloader: R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x",
+ debug(8, "elfloader: R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x",
i, a, origTarget, *target);
}
diff --git a/backends/plugins/sdl/sdl-provider.cpp b/backends/plugins/sdl/sdl-provider.cpp
index be04b5a1eb..6f8ca594f6 100644
--- a/backends/plugins/sdl/sdl-provider.cpp
+++ b/backends/plugins/sdl/sdl-provider.cpp
@@ -25,6 +25,9 @@
#if defined(DYNAMIC_MODULES) && defined(SDL_BACKEND)
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "backends/plugins/sdl/sdl-provider.h"
#include "backends/plugins/dynamic-plugin.h"
#include "common/fs.h"
diff --git a/backends/timer/psp/timer.cpp b/backends/timer/psp/timer.cpp
index 55b63ba4fd..2b6bebe37c 100644
--- a/backends/timer/psp/timer.cpp
+++ b/backends/timer/psp/timer.cpp
@@ -23,7 +23,7 @@
*
*/
-#if defined (__PSP__)
+#if defined (__PSP__)
#include <pspthreadman.h>
#include "common/scummsys.h"
@@ -38,7 +38,7 @@
bool PspTimer::start() {
DEBUG_ENTER_FUNC();
-
+
if (!_interval || !_callback)
return false;
@@ -48,31 +48,31 @@ bool PspTimer::start() {
PSP_ERROR("failed to create timer thread. Error code %d\n", _threadId);
return false;
}
-
+
PspTimer *_this = this; // trick to get into context when the thread starts
_init = true;
-
+
if (sceKernelStartThread(_threadId, sizeof(uint32 *), &_this) < 0) {
PSP_ERROR("failed to start thread %d\n", _threadId);
return false;
}
-
+
PSP_DEBUG_PRINT("created timer thread[%x]\n", _threadId);
-
+
return true;
}
int PspTimer::thread(SceSize, void *__this) {
DEBUG_ENTER_FUNC();
PspTimer *_this = *(PspTimer **)__this; // get our this for the context
-
+
_this->timerThread();
return 0;
};
void PspTimer::timerThread() {
DEBUG_ENTER_FUNC();
-
+
while (_init) {
sceKernelDelayThread(_interval);
PSP_DEBUG_PRINT("calling callback!\n");
diff --git a/backends/timer/psp/timer.h b/backends/timer/psp/timer.h
index ec31addb72..01b1ca636e 100644
--- a/backends/timer/psp/timer.h
+++ b/backends/timer/psp/timer.h
@@ -24,8 +24,8 @@
*/
#ifndef PSP_TIMER_H
-#define PSP_TIMER_H
-
+#define PSP_TIMER_H
+
class PspTimer {
public:
typedef void (* CallbackFunc)(void);
@@ -44,4 +44,4 @@ private:
bool _init;
};
-#endif
+#endif // PSP_TIMER_H
diff --git a/backends/vkeybd/packs/vkeybdpack.py b/backends/vkeybd/packs/vkeybdpack.py
index 130e4b737b..b0cd4c45e9 100755
--- a/backends/vkeybd/packs/vkeybdpack.py
+++ b/backends/vkeybd/packs/vkeybdpack.py
@@ -16,9 +16,9 @@ def buildPack(packName):
if not os.path.isdir(packName):
print ("Invalid pack name: " + packName)
return
-
+
zf = zipfile.ZipFile(packName + ".zip", 'w')
-
+
zf.compress_type = zipfile.ZIP_DEFLATED
print ("Building '" + packName + "' pack:")
@@ -28,9 +28,9 @@ def buildPack(packName):
if os.path.isfile(filename) and not filename[0] == '.' and filename.endswith(PACK_FILE_EXTENSIONS):
zf.write(filename, './' + filename, compress_type=compression)
print (" Adding file: " + filename)
-
+
os.chdir('../')
-
+
zf.close()
def buildAllPacks():
@@ -49,13 +49,13 @@ def printUsage():
print (" Builds the pack called 'packname'.\n")
def main():
-
+
if len(sys.argv) == 2 and sys.argv[1] == "makeall":
buildAllPacks()
-
+
elif len(sys.argv) == 3 and sys.argv[1] == "make":
buildPack(sys.argv[2])
-
+
else:
printUsage()
diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp
index 6a644e0cdf..dd11866262 100644
--- a/backends/vkeybd/virtual-keyboard-parser.cpp
+++ b/backends/vkeybd/virtual-keyboard-parser.cpp
@@ -270,7 +270,7 @@ bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) {
int r, g, b;
if (node->values.contains("transparent_color")) {
- if (!parseIntegerKey(node->values["transparent_color"].c_str(), 3, &r, &g, &b))
+ if (!parseIntegerKey(node->values["transparent_color"], 3, &r, &g, &b))
return parserError("Could not parse color value");
} else {
// default to purple
@@ -281,7 +281,7 @@ bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) {
_mode->transparentColor = format.RGBToColor(r, g, b);
if (node->values.contains("display_font_color")) {
- if (!parseIntegerKey(node->values["display_font_color"].c_str(), 3, &r, &g, &b))
+ if (!parseIntegerKey(node->values["display_font_color"], 3, &r, &g, &b))
return parserError("Could not parse color value");
} else {
r = g = b = 0; // default to black
@@ -336,7 +336,7 @@ byte VirtualKeyboardParser::parseFlags(const String& flags) {
bool VirtualKeyboardParser::parseRect(Rect &rect, const String& coords) {
int x1, y1, x2, y2;
- if (!parseIntegerKey(coords.c_str(), 4, &x1, &y1, &x2, &y2))
+ if (!parseIntegerKey(coords, 4, &x1, &y1, &x2, &y2))
return parserError("Invalid coords for rect area");
rect.left = x1;
rect.top = y1;
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 2f4e78fd80..cf59ae6dfd 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -219,6 +219,14 @@ void registerDefaults() {
ConfMan.registerDefault("record_file_name", "record.bin");
ConfMan.registerDefault("record_temp_file_name", "record.tmp");
ConfMan.registerDefault("record_time_file_name", "record.time");
+
+#if 0
+ // NEW CODE TO HIDE CONSOLE FOR WIN32
+#ifdef WIN32
+ // console hiding for win32
+ ConfMan.registerDefault("show_console", false);
+#endif
+#endif
}
//
@@ -546,6 +554,15 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
END_OPTION
#endif
+#if 0
+ // NEW CODE TO HIDE CONSOLE FOR WIN32
+#ifdef WIN32
+ // console hiding for win32
+ DO_LONG_OPTION_BOOL("show-console")
+ END_OPTION
+#endif
+#endif
+
unknownOption:
// If we get till here, the option is unhandled and hence unknown.
usage("Unrecognized option '%s'", argv[i]);
@@ -578,6 +595,10 @@ static void listTargets() {
using namespace Common;
const ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
ConfigManager::DomainMap::const_iterator iter;
+
+ Common::Array<Common::String> targets;
+ targets.reserve(domains.size());
+
for (iter = domains.begin(); iter != domains.end(); ++iter) {
Common::String name(iter->_key);
Common::String description(iter->_value.getVal("description"));
@@ -592,9 +613,13 @@ static void listTargets() {
description = g.description();
}
- printf("%-20s %s\n", name.c_str(), description.c_str());
-
+ targets.push_back(Common::String::printf("%-20s %s", name.c_str(), description.c_str()));
}
+
+ Common::sort(targets.begin(), targets.end());
+
+ for (Common::Array<Common::String>::const_iterator i = targets.begin(), end = targets.end(); i != end; ++i)
+ printf("%s\n", i->c_str());
}
/** List all saves states for the given target. */
@@ -950,9 +975,9 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin
if (dir && *dir && strlen(dir) < MAXPATHLEN) {
Common::FSNode saveDir(dir);
if (!saveDir.exists()) {
- warning("Non-existent SCUMMVM_SAVEPATH save path. It will be ignored.");
+ warning("Non-existent SCUMMVM_SAVEPATH save path. It will be ignored");
} else if (!saveDir.isWritable()) {
- warning("Non-writable SCUMMVM_SAVEPATH save path. It will be ignored.");
+ warning("Non-writable SCUMMVM_SAVEPATH save path. It will be ignored");
} else {
settings["savepath"] = dir;
}
diff --git a/base/internal_version.h b/base/internal_version.h
index 5fd0535142..2b00ce60d2 100644
--- a/base/internal_version.h
+++ b/base/internal_version.h
@@ -3,7 +3,7 @@
#endif
#ifndef SCUMMVM_SVN_REVISION
-#define SCUMMVM_SVN_REVISION
+#define SCUMMVM_SVN_REVISION ""
#endif
#ifdef RELEASE_BUILD
@@ -11,4 +11,4 @@
#define SCUMMVM_SVN_REVISION
#endif
-#define SCUMMVM_VERSION "1.2.0svn" SCUMMVM_SVN_REVISION
+#define SCUMMVM_VERSION "1.3.0svn" SCUMMVM_SVN_REVISION
diff --git a/base/internal_version.h.in b/base/internal_version.h.in
index adf5f94d21..dacaf72d40 100644
--- a/base/internal_version.h.in
+++ b/base/internal_version.h.in
@@ -3,7 +3,7 @@
#endif
#ifndef SCUMMVM_SVN_REVISION
-#define SCUMMVM_SVN_REVISION
+#define SCUMMVM_SVN_REVISION "@SVN_REVISION@"
#endif
#ifdef RELEASE_BUILD
diff --git a/base/main.cpp b/base/main.cpp
index b925391a96..37e100b49f 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -315,7 +315,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
Common::StringMap settings;
command = Base::parseCommandLine(settings, argc, argv);
- // Load the config file (possibly overriden via command line):
+ // Load the config file (possibly overridden via command line):
if (settings.contains("config")) {
ConfMan.loadConfigFile(settings["config"]);
settings.erase("config");
@@ -326,9 +326,6 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
// Update the config file
ConfMan.set("versioninfo", gScummVMVersion, Common::ConfigManager::kApplicationDomain);
- // Enable translation
- TransMan.setLanguage(ConfMan.get("gui_language").c_str());
-
// Load and setup the debuglevel and the debug flags. We do this at the
// soonest possible moment to ensure debug output starts early on, if
// requested.
@@ -440,9 +437,6 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
PluginManager::instance().loadPlugins();
#endif
} else {
- // A dialog would be nicer, but we don't have any
- // screen to draw on yet.
- warning("%s", _("Could not find any engine capable of running the selected game"));
GUI::displayErrorDialog(_("Could not find any engine capable of running the selected game"));
}
diff --git a/base/plugins.cpp b/base/plugins.cpp
index 2e08dabc3f..d2fad34579 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -121,6 +121,9 @@ public:
#if PLUGIN_ENABLED_STATIC(KYRA)
LINK_PLUGIN(KYRA)
#endif
+ #if PLUGIN_ENABLED_STATIC(LASTEXPRESS)
+ LINK_PLUGIN(LASTEXPRESS)
+ #endif
#if PLUGIN_ENABLED_STATIC(LURE)
LINK_PLUGIN(LURE)
#endif
@@ -154,12 +157,21 @@ public:
#if PLUGIN_ENABLED_STATIC(SWORD2)
LINK_PLUGIN(SWORD2)
#endif
+ #if PLUGIN_ENABLED_STATIC(SWORD25)
+ LINK_PLUGIN(SWORD25)
+ #endif
#if PLUGIN_ENABLED_STATIC(TEENAGENT)
LINK_PLUGIN(TEENAGENT)
#endif
+ #if PLUGIN_ENABLED_STATIC(TESTBED)
+ LINK_PLUGIN(TESTBED)
+ #endif
#if PLUGIN_ENABLED_STATIC(TINSEL)
LINK_PLUGIN(TINSEL)
#endif
+ #if PLUGIN_ENABLED_STATIC(TOON)
+ LINK_PLUGIN(TOON)
+ #endif
#if PLUGIN_ENABLED_STATIC(TOUCHE)
LINK_PLUGIN(TOUCHE)
#endif
@@ -203,13 +215,14 @@ public:
LINK_PLUGIN(ADLIB)
LINK_PLUGIN(PCSPK)
LINK_PLUGIN(PCJR)
+ LINK_PLUGIN(CMS)
#ifndef DISABLE_SID
LINK_PLUGIN(C64)
#endif
LINK_PLUGIN(AMIGA)
LINK_PLUGIN(APPLEIIGS)
LINK_PLUGIN(TOWNS)
- #if defined (UNIX)
+ #if defined(USE_TIMIDITY)
LINK_PLUGIN(TIMIDITY)
#endif
diff --git a/base/version.cpp b/base/version.cpp
index 055067a656..6967263f5c 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -92,6 +92,10 @@ const char *gScummVMFeatures = ""
"SEQ "
#endif
+#ifdef USE_TIMIDITY
+ "TiMidity "
+#endif
+
#ifdef USE_RGB_COLOR
"RGB "
#endif
@@ -107,5 +111,9 @@ const char *gScummVMFeatures = ""
#ifdef USE_FLUIDSYNTH
"FluidSynth "
#endif
+
+#ifdef USE_THEORADEC
+ "Theora "
+#endif
;
diff --git a/common/algorithm.h b/common/algorithm.h
index 9d22af4090..b34d6f852d 100644
--- a/common/algorithm.h
+++ b/common/algorithm.h
@@ -242,13 +242,17 @@ void sort(T first, T last) {
*/
template<class T>
T gcd(T a, T b) {
- if (a <= 0) a = -a;
- if (b <= 0) b = -b;
+ if (a <= 0)
+ a = -a;
+ if (b <= 0)
+ b = -b;
+
while (a > 0) {
T tmp = a;
a = b % a;
b = tmp;
}
+
return b;
}
diff --git a/common/array.h b/common/array.h
index 4cc5369f9f..e3aab66dc6 100644
--- a/common/array.h
+++ b/common/array.h
@@ -150,6 +150,12 @@ public:
insert_aux(_storage + idx, &element, &element + 1);
}
+ void insert_at(int idx, const Array<T> &array) {
+ assert(idx >= 0 && (uint)idx <= _size);
+ insert_aux(_storage + idx, array.begin(), array.end());
+ }
+
+
T remove_at(int idx) {
assert(idx >= 0 && (uint)idx < _size);
T tmp = _storage[idx];
diff --git a/common/forbidden.h b/common/forbidden.h
new file mode 100644
index 0000000000..cc71c36711
--- /dev/null
+++ b/common/forbidden.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 COMMON_FORBIDDEN_H
+#define COMMON_FORBIDDEN_H
+
+
+//
+// Backend files may #define FORBIDDEN_SYMBOL_ALLOW_ALL if they
+// have to access functions like fopen, fread etc.
+// Regular code, esp. code in engines/, should never do that.
+//
+
+#ifndef FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#define FORBIDDEN_SYMBOL_REPLACEMENT FORBIDDEN SYMBOL!
+
+
+/*
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_printf
+#undef printf
+#define printf FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fprintf
+#undef fprintf
+#define fprintf FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+*/
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#undef FILE
+#define FILE FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fopen
+#undef fopen
+#define fopen(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fclose
+#undef fclose
+#define fclose(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fread
+#undef fread
+#define fread(a,b,c,d) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fwrite
+#undef fwrite
+#define fwrite(a,b,c,d) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fseek
+#undef fseek
+#define fseek(a,b,c) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_ftell
+#undef ftell
+#define ftell(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_feof
+#undef feof
+#define feof(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fgetc
+#undef fgetc
+#define fgetc(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_fputc
+#undef fputc
+#define fputc(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_setjmp
+#undef setjmp
+#define setjmp(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_longjmp
+#undef longjmp
+#define longjmp(a,b) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+#ifndef FORBIDDEN_SYMBOL_EXCEPTION_system
+#undef system
+#define system(a) FORBIDDEN_SYMBOL_REPLACEMENT
+#endif
+
+
+/*
+time_t
+
+time
+
+difftime
+
+mktime
+
+localtime
+
+clock
+
+gmtime
+
+system
+
+remove
+
+setlocale
+
+setvbuf
+*/
+
+#endif
+
+
+#endif
diff --git a/common/hashmap.h b/common/hashmap.h
index 0d4d7663f3..45192256a9 100644
--- a/common/hashmap.h
+++ b/common/hashmap.h
@@ -42,7 +42,8 @@ namespace Common {
// The sgi IRIX MIPSpro Compiler has difficulties with nested templates.
// This and the other __sgi conditionals below work around these problems.
-#if defined(__sgi) && !defined(__GNUC__)
+// The Intel C++ Compiler suffers from the same problems.
+#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
template<class T> class IteratorImpl;
#endif
@@ -138,7 +139,7 @@ private:
template<class NodeType>
class IteratorImpl {
friend class HashMap;
-#if defined(__sgi) && !defined(__GNUC__)
+#if (defined(__sgi) && !defined(__GNUC__)) || defined(__INTEL_COMPILER)
template<class T> friend class Common::IteratorImpl;
#else
template<class T> friend class IteratorImpl;
diff --git a/common/macresman.cpp b/common/macresman.cpp
index eb6a5939b6..641702b5ec 100644
--- a/common/macresman.cpp
+++ b/common/macresman.cpp
@@ -110,6 +110,7 @@ bool MacResManager::open(Common::String filename) {
_baseFileName = filename;
return true;
}
+
delete macResForkRawStream;
#endif
@@ -169,6 +170,7 @@ bool MacResManager::open(Common::FSNode path, Common::String filename) {
_baseFileName = filename;
return true;
}
+
delete macResForkRawStream;
#endif
@@ -466,6 +468,28 @@ Common::SeekableReadStream *MacResManager::getResource(const Common::String &fil
return 0;
}
+Common::SeekableReadStream *MacResManager::getResource(uint32 typeID, const Common::String &filename) {
+ for (uint32 i = 0; i < _resMap.numTypes; i++) {
+ if (_resTypes[i].id != typeID)
+ continue;
+
+ for (uint32 j = 0; j < _resTypes[i].items; j++) {
+ if (_resLists[i][j].nameOffset != -1 && filename.equalsIgnoreCase(_resLists[i][j].name)) {
+ _stream->seek(_dataOffset + _resLists[i][j].dataOffset);
+ uint32 len = _stream->readUint32BE();
+
+ // Ignore resources with 0 length
+ if (!len)
+ return 0;
+
+ return _stream->readStream(len);
+ }
+ }
+ }
+
+ return 0;
+}
+
void MacResManager::readMap() {
_stream->seek(_mapOffset + 22);
diff --git a/common/macresman.h b/common/macresman.h
index 2168235670..d47b0ca329 100644
--- a/common/macresman.h
+++ b/common/macresman.h
@@ -57,7 +57,7 @@ public:
/**
* Read resource from the Mac Binary file
- * @param typeID FourCC with type ID
+ * @param typeID FourCC of the type
* @param resID Resource ID to fetch
* @return Pointer to a SeekableReadStream with loaded resource
*/
@@ -65,11 +65,20 @@ public:
/**
* Read resource from the Mac Binary file
+ * @note This will take the first resource that matches this name, regardless of type
* @param filename filename of the resource
* @return Pointer to a SeekableReadStream with loaded resource
*/
Common::SeekableReadStream *getResource(const Common::String &filename);
+ /**
+ * Read resource from the Mac Binary file
+ * @param typeID FourCC of the type
+ * @param filename filename of the resource
+ * @return Pointer to a SeekableReadStream with loaded resource
+ */
+ Common::SeekableReadStream *getResource(uint32 typeID, const Common::String &filename);
+
Common::SeekableReadStream *getDataFork();
Common::String getResName(uint32 typeID, uint16 resID);
uint32 getResForkSize();
diff --git a/common/memorypool.h b/common/memorypool.h
index 7188e854f9..e19d982913 100644
--- a/common/memorypool.h
+++ b/common/memorypool.h
@@ -66,7 +66,7 @@ public:
* Constructor for a memory pool with the given chunk size.
* @param chunkSize the chunk size of this memory pool
*/
- MemoryPool(size_t chunkSize);
+ explicit MemoryPool(size_t chunkSize);
~MemoryPool();
/**
diff --git a/common/mutex.h b/common/mutex.h
index ba5c45c30e..3addaa6b18 100644
--- a/common/mutex.h
+++ b/common/mutex.h
@@ -49,8 +49,8 @@ class StackLock {
void lock();
void unlock();
public:
- StackLock(MutexRef mutex, const char *mutexName = NULL);
- StackLock(const Mutex &mutex, const char *mutexName = NULL);
+ explicit StackLock(MutexRef mutex, const char *mutexName = NULL);
+ explicit StackLock(const Mutex &mutex, const char *mutexName = NULL);
~StackLock();
};
diff --git a/common/rational.cpp b/common/rational.cpp
index 163044f349..f55c2dcfe3 100644
--- a/common/rational.cpp
+++ b/common/rational.cpp
@@ -22,6 +22,7 @@
* $Id$
*/
+#include "common/debug.h"
#include "common/rational.h"
#include "common/util.h"
#include "common/algorithm.h"
@@ -257,38 +258,34 @@ frac_t Rational::toFrac() const {
return (_num * FRAC_ONE) / _denom;
}
-Rational::operator int() const {
- return toInt();
-}
-
-Rational::operator double() const {
- return toDouble();
-}
-
const Rational operator+(int left, const Rational &right) {
- Rational tmp = right;
- tmp += left;
+ Rational tmp(left);
+ tmp += right;
return tmp;
}
const Rational operator-(int left, const Rational &right) {
- Rational tmp = right;
- tmp -= left;
+ Rational tmp(left);
+ tmp -= right;
return tmp;
}
const Rational operator*(int left, const Rational &right) {
- Rational tmp = right;
- tmp *= left;
+ Rational tmp(left);
+ tmp *= right;
return tmp;
}
const Rational operator/(int left, const Rational &right) {
- Rational tmp = right;
- tmp /= left;
+ Rational tmp(left);
+ tmp /= right;
return tmp;
}
+void Rational::debugPrint(int debuglevel, const char *caption) const {
+ debug(debuglevel, "%s %d/%d", caption, _num, _denom);
+}
+
bool operator==(int left, const Rational &right) {
return right == left;
}
diff --git a/common/rational.h b/common/rational.h
index 5ceac36209..bee09d8ddb 100644
--- a/common/rational.h
+++ b/common/rational.h
@@ -76,9 +76,6 @@ public:
bool operator>=(int right) const;
bool operator<=(int right) const;
- operator int() const;
- operator double() const;
-
void invert();
Rational getInverse() const;
@@ -86,6 +83,8 @@ public:
double toDouble() const;
frac_t toFrac() const;
+ void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const;
+
private:
int _num;
int _denom;
diff --git a/common/scummsys.h b/common/scummsys.h
index df8b8e1901..d168544b18 100644
--- a/common/scummsys.h
+++ b/common/scummsys.h
@@ -243,7 +243,7 @@
#define SCUMM_NEED_ALIGNMENT
#endif
- // Very BAD hack following, used to avoid triggering an assert in uClibc dingux library
+ // Very BAD hack following, used to avoid triggering an assert in uClibc dingux library
// "toupper" when pressing keyboard function keys.
#if defined(DINGUX)
#undef toupper
@@ -315,6 +315,7 @@
#elif defined(__PSP__)
#include <malloc.h>
+ #include "backends/platform/psp/memory.h"
#define scumm_stricmp strcasecmp
#define scumm_strnicmp strncasecmp
@@ -322,6 +323,9 @@
#define SCUMM_LITTLE_ENDIAN
#define SCUMM_NEED_ALIGNMENT
+ /* to make an efficient, inlined memcpy implementation */
+ #define memcpy(dst, src, size) psp_memcpy(dst, src, size)
+
#elif defined(__amigaos4__)
#define scumm_stricmp strcasecmp
@@ -433,5 +437,6 @@
typedef uint16 OverlayColor;
#endif
+#include <common/forbidden.h>
#endif
diff --git a/common/str.cpp b/common/str.cpp
index 2961a0c61b..c3c19adfe6 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -439,12 +439,20 @@ String String::printf(const char *fmt, ...) {
int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
va_end(va);
- if (len == -1) {
- // MSVC doesn't return the size the full string would take up.
- // Try increasing the size of the string until it fits.
+ if (len == -1 || len == _builtinCapacity - 1) {
+ // MSVC and IRIX don't return the size the full string would take up.
+ // MSVC returns -1, IRIX returns the number of characters actually written,
+ // which is at the most the size of the buffer minus one, as the string is
+ // truncated to fit.
// We assume MSVC failed to output the correct, null-terminated string
// if the return value is either -1 or size.
+ // For IRIX, because we lack a better mechanism, we assume failure
+ // if the return value equals size - 1.
+ // The downside to this is that whenever we try to format a string where the
+ // size is 1 below the built-in capacity, the size is needlessly increased.
+
+ // Try increasing the size of the string until it fits.
int size = _builtinCapacity;
do {
size *= 2;
@@ -455,7 +463,7 @@ String String::printf(const char *fmt, ...) {
va_start(va, fmt);
len = vsnprintf(output._str, size, fmt, va);
va_end(va);
- } while (len == -1 || len >= size);
+ } while (len == -1 || len >= size - 1);
output._size = len;
} else if (len < (int)_builtinCapacity) {
// vsnprintf succeeded
@@ -691,9 +699,18 @@ bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMod
switch (*pat) {
case '*':
- // Record pattern / string position for backtracking
- p = ++pat;
- q = str;
+ if (*str) {
+ // Record pattern / string position for backtracking
+ p = ++pat;
+ q = str;
+ } else {
+ // If we've reached the end of str, we can't backtrack further
+ // NB: We can't simply check if pat also ended here, because
+ // the pattern might end with any number of *s.
+ ++pat;
+ p = 0;
+ q = 0;
+ }
// If pattern ended with * -> match
if (!*pat)
return true;
diff --git a/common/stream.cpp b/common/stream.cpp
index 718c806ff3..9f8f6127f1 100644
--- a/common/stream.cpp
+++ b/common/stream.cpp
@@ -231,21 +231,13 @@ BufferedReadStream::BufferedReadStream(ReadStream *parentStream, uint32 bufSize,
_realBufSize(bufSize) {
assert(parentStream);
- allocBuf(bufSize);
- assert(_buf);
-}
-
-void BufferedReadStream::allocBuf(uint32 bufSize) {
_buf = new byte[bufSize];
+ assert(_buf);
}
BufferedReadStream::~BufferedReadStream() {
if (_disposeParentStream)
delete _parentStream;
- deallocBuf();
-}
-
-void BufferedReadStream::deallocBuf() {
delete[] _buf;
}
@@ -294,7 +286,7 @@ uint32 BufferedReadStream::read(void *dataPtr, uint32 dataSize) {
// Satisfy the request from the buffer
memcpy(dataPtr, _buf + _pos, dataSize);
_pos += dataSize;
- }
+ }
return alreadyRead + dataSize;
}
@@ -309,7 +301,7 @@ bool BufferedSeekableReadStream::seek(int32 offset, int whence) {
// Note: We could try to handle SEEK_END and SEEK_SET, too, but
// since they are rarely used, it seems not worth the effort.
_eos = false; // seeking always cancels EOS
-
+
if (whence == SEEK_CUR && (int)_pos + offset >= 0 && _pos + offset <= _bufSize) {
_pos += offset;
@@ -334,24 +326,16 @@ BufferedWriteStream::BufferedWriteStream(WriteStream *parentStream, uint32 bufSi
_bufSize(bufSize) {
assert(parentStream);
- allocBuf(bufSize);
+ _buf = new byte[bufSize];
assert(_buf);
}
BufferedWriteStream::~BufferedWriteStream() {
assert(flush());
-
+
if (_disposeParentStream)
delete _parentStream;
-
- deallocBuf();
-}
-
-void BufferedWriteStream::allocBuf(uint32 bufSize) {
- _buf = new byte[bufSize];
-}
-void BufferedWriteStream::deallocBuf() {
delete[] _buf;
}
@@ -359,7 +343,7 @@ uint32 BufferedWriteStream::write(const void *dataPtr, uint32 dataSize) {
// check if we have enough space for writing to the buffer
if (_bufSize - _pos >= dataSize) {
memcpy(_buf + _pos, dataPtr, dataSize);
- _pos += dataSize;
+ _pos += dataSize;
} else if (_bufSize >= dataSize) { // check if we can flush the buffer and load the data
// flush the buffer
assert(flushBuffer());
@@ -367,7 +351,7 @@ uint32 BufferedWriteStream::write(const void *dataPtr, uint32 dataSize) {
_pos += dataSize;
} else { // too big for our buffer
// flush the buffer
- assert(flushBuffer());
+ assert(flushBuffer());
return _parentStream->write(dataPtr, dataSize);
}
return dataSize;
@@ -375,7 +359,7 @@ uint32 BufferedWriteStream::write(const void *dataPtr, uint32 dataSize) {
bool BufferedWriteStream::flushBuffer() {
uint32 bytesToWrite = _pos;
-
+
if (bytesToWrite) {
_pos = 0;
if (_parentStream->write(_buf, bytesToWrite) != bytesToWrite)
diff --git a/common/stream.h b/common/stream.h
index 4d673d1539..c6605cb42d 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -497,8 +497,6 @@ protected:
bool _eos; // end of stream
uint32 _bufSize;
uint32 _realBufSize;
- virtual void allocBuf(uint32 bufSize); // virtual functions to allocate/deallocate the buffer
- virtual void deallocBuf();
public:
BufferedReadStream(ReadStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO);
@@ -538,13 +536,11 @@ protected:
uint32 _pos;
uint32 _bufSize;
bool flushBuffer(); // write out the data in the buffer
- virtual void allocBuf(uint32 bufSize); // virtual functions to allocate/deallocate the buffer
- virtual void deallocBuf();
public:
BufferedWriteStream(WriteStream *parentStream, uint32 bufSize, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO);
virtual ~BufferedWriteStream();
-
+
virtual uint32 write(const void *dataPtr, uint32 dataSize);
virtual bool flush();
};
diff --git a/common/translation.cpp b/common/translation.cpp
index 1328ed62d2..a33e1a5243 100644
--- a/common/translation.cpp
+++ b/common/translation.cpp
@@ -213,6 +213,12 @@ const char *TranslationManager::getCurrentCharset() {
return _currentCharset.c_str();
}
+const char *TranslationManager::getCurrentLanguage() {
+ if (_currentLang == -1)
+ return "C";
+ return _langs[_currentLang].c_str();
+}
+
String TranslationManager::getTranslation(const String &message) {
return getTranslation(message.c_str());
}
@@ -264,18 +270,18 @@ bool TranslationManager::openTranslationsFile(File& inFile) {
// First try to open it directly (i.e. using the SearchMan).
if (inFile.open("translations.dat"))
return true;
-
+
// Then look in the Themepath if we can find the file.
if (ConfMan.hasKey("themepath"))
return openTranslationsFile(FSNode(ConfMan.get("themepath")), inFile);
-
+
return false;
}
bool TranslationManager::openTranslationsFile(const FSNode &node, File& inFile, int depth) {
if (!node.exists() || !node.isReadable() || !node.isDirectory())
return false;
-
+
// Check if we can find the file in this directory
// Since File::open(FSNode) makes all the needed tests, it is not really
// necessary to make them here. But it avoid printing warnings.
@@ -284,21 +290,21 @@ bool TranslationManager::openTranslationsFile(const FSNode &node, File& inFile,
if (inFile.open(fileNode))
return true;
}
-
+
// Check if we exceeded the given recursion depth
if (depth - 1 == -1)
- return false;
-
+ return false;
+
// Otherwise look for it in sub-directories
FSList fileList;
if (!node.getChildren(fileList, FSNode::kListDirectoriesOnly))
return false;
-
+
for (FSList::iterator i = fileList.begin(); i != fileList.end(); ++i) {
if (openTranslationsFile(*i, inFile, depth == -1 ? - 1 : depth - 1))
return true;
}
-
+
// Not found in this directory or its sub-directories
return false;
}
@@ -318,7 +324,7 @@ void TranslationManager::loadTranslationsInfoDat() {
// Get number of translations
int nbTranslations = in.readUint16BE();
-
+
// Skip all the block sizes
for (int i = 0; i < nbTranslations + 2; ++i)
in.readUint16BE();
@@ -467,12 +473,15 @@ String TranslationManager::getTranslation(const String &message, const String &)
const TLangArray TranslationManager::getSupportedLanguageNames() const {
return TLangArray();
}
-
+
const char *TranslationManager::getCurrentCharset() {
return "ASCII";
}
+const char *TranslationManager::getCurrentLanguage() {
+ return "C";
+}
+
#endif // USE_TRANSLATION
} // End of namespace Common
-
diff --git a/common/translation.h b/common/translation.h
index 57c4cc2ac8..ff0a8a2acf 100644
--- a/common/translation.h
+++ b/common/translation.h
@@ -116,7 +116,7 @@ public:
* it returns the original untranslated message.
*/
String getTranslation(const String &message);
-
+
/**
* Returns the translation into the current language of the parameter
* message. In case the message isn't found in the translation catalog,
@@ -127,7 +127,7 @@ public:
* massage without a context or with a different context.
*/
const char *getTranslation(const char *message, const char *context);
-
+
/**
* Returns the translation into the current language of the parameter
* message. In case the message isn't found in the translation catalog,
@@ -151,6 +151,11 @@ public:
*/
const char *getCurrentCharset();
+ /**
+ * Returns currently selected translation language
+ */
+ const char *getCurrentLanguage();
+
private:
#ifdef USE_TRANSLATION
/**
@@ -208,6 +213,6 @@ private:
#define _s(str) str
#define _sc(str, ctxt) str
-#define DECLARE_TRANSLATION_ADDITIONAL_CONTEXT(str, ctxt)
+#define DECLARE_TRANSLATION_ADDITIONAL_CONTEXT(str, ctxt)
#endif
diff --git a/common/unzip.cpp b/common/unzip.cpp
index 6df2ed4848..3f084ad861 100644
--- a/common/unzip.cpp
+++ b/common/unzip.cpp
@@ -373,7 +373,7 @@ typedef struct {
unz_file_info_internal cur_file_info_internal; /* private info about it*/
} cached_file_in_zip;
-typedef Common::HashMap<Common::String, cached_file_in_zip, Common::IgnoreCase_Hash,
+typedef Common::HashMap<Common::String, cached_file_in_zip, Common::IgnoreCase_Hash,
Common::IgnoreCase_EqualTo> ZipHash;
/* unz_s contain internal information about the zipfile
@@ -401,7 +401,7 @@ typedef struct {
/* ===========================================================================
Read a byte from a gz_stream; update next_in and avail_in. Return EOF
for end of file.
- IN assertion: the stream s has been sucessfully opened for reading.
+ IN assertion: the stream s has been successfully opened for reading.
*/
diff --git a/common/util.h b/common/util.h
index 52e4295bbb..699653918a 100644
--- a/common/util.h
+++ b/common/util.h
@@ -63,6 +63,20 @@ template<typename T> inline void SWAP(T &a, T &b) { T tmp = a; a = b; b = tmp; }
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+/**
+ * @def SCUMMVM_CURRENT_FUNCTION
+ * This macro evaluates to the current function's name on compilers supporting this.
+ */
+#if defined(__GNUC__)
+# define SCUMMVM_CURRENT_FUNCTION __PRETTY_FUNCTION__
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
+# define SCUMMVM_CURRENT_FUNCTION __func__
+#elif defined(_MSC_VER) && _MSC_VER >= 1300
+# define SCUMMVM_CURRENT_FUNCTION __FUNCTION__
+#else
+# define SCUMMVM_CURRENT_FUNCTION "<unknown>"
+#endif
+
namespace Common {
/**
diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp
index 1c652fa19d..ab56ba3751 100644
--- a/common/xmlparser.cpp
+++ b/common/xmlparser.cpp
@@ -229,6 +229,47 @@ bool XMLParser::parseKeyValue(Common::String keyName) {
return true;
}
+bool XMLParser::parseIntegerKey(const char *key, int count, ...) {
+ bool result;
+ va_list args;
+ va_start(args, count);
+ result = vparseIntegerKey(key, count, args);
+ va_end(args);
+ return result;
+}
+
+bool XMLParser::parseIntegerKey(const Common::String &key, int count, ...) {
+ bool result;
+ va_list args;
+ va_start(args, count);
+ result = vparseIntegerKey(key.c_str(), count, args);
+ va_end(args);
+ return result;
+}
+
+bool XMLParser::vparseIntegerKey(const char *key, int count, va_list args) {
+ char *parseEnd;
+ int *num_ptr;
+
+ while (count--) {
+ while (isspace(*key))
+ key++;
+
+ num_ptr = va_arg(args, int*);
+ *num_ptr = strtol(key, &parseEnd, 10);
+
+ key = parseEnd;
+
+ while (isspace(*key))
+ key++;
+
+ if (count && *key++ != ',')
+ return false;
+ }
+
+ return (*key == 0);
+}
+
bool XMLParser::closeKey() {
bool ignore = false;
bool result = true;
@@ -410,5 +451,61 @@ bool XMLParser::parse() {
return true;
}
+bool XMLParser::skipSpaces() {
+ if (!isspace(_char))
+ return false;
+
+ while (_char && isspace(_char))
+ _char = _stream->readByte();
+
+ return true;
+}
+
+bool XMLParser::skipComments() {
+ if (_char == '<') {
+ _char = _stream->readByte();
+
+ if (_char != '!') {
+ _stream->seek(-1, SEEK_CUR);
+ _char = '<';
+ return false;
+ }
+
+ if (_stream->readByte() != '-' || _stream->readByte() != '-')
+ return parserError("Malformed comment syntax.");
+
+ _char = _stream->readByte();
+
+ while (_char) {
+ if (_char == '-') {
+ if (_stream->readByte() == '-') {
+
+ if (_stream->readByte() != '>')
+ return parserError("Malformed comment (double-hyphen inside comment body).");
+
+ _char = _stream->readByte();
+ return true;
+ }
+ }
+
+ _char = _stream->readByte();
+ }
+
+ return parserError("Comment has no closure.");
+ }
+
+ return false;
+}
+
+bool XMLParser::parseToken() {
+ _token.clear();
+
+ while (isValidNameChar(_char)) {
+ _token += _char;
+ _char = _stream->readByte();
+ }
+
+ return isspace(_char) != 0 || _char == '>' || _char == '=' || _char == '/';
}
+} // End of namespace Common
diff --git a/common/xmlparser.h b/common/xmlparser.h
index 5271917927..c8cc349a6c 100644
--- a/common/xmlparser.h
+++ b/common/xmlparser.h
@@ -293,65 +293,22 @@ protected:
/**
* Prints an error message when parsing fails and stops the parser.
- * Parser error always returns "false" so we can pass the return value directly
- * and break down the parsing.
+ * Parser error always returns "false" so we can pass the return value
+ * directly and break down the parsing.
*/
bool parserError(const char *errorString, ...) GCC_PRINTF(2, 3);
/**
- * Skips spaces/whitelines etc. Returns true if any spaces were skipped.
+ * Skips spaces/whitelines etc.
+ * @return true if any spaces were skipped.
*/
- bool skipSpaces() {
- if (!isspace(_char))
- return false;
-
- while (_char && isspace(_char))
- _char = _stream->readByte();
-
- return true;
- }
+ bool skipSpaces();
/**
* Skips comment blocks and comment lines.
- * Returns true if any comments were skipped.
- * Overload this if you want to disable comments on your XML syntax
- * or to change the commenting syntax.
+ * @return true if any comments were skipped.
*/
- virtual bool skipComments() {
- if (_char == '<') {
- _char = _stream->readByte();
-
- if (_char != '!') {
- _stream->seek(-1, SEEK_CUR);
- _char = '<';
- return false;
- }
-
- if (_stream->readByte() != '-' || _stream->readByte() != '-')
- return parserError("Malformed comment syntax.");
-
- _char = _stream->readByte();
-
- while (_char) {
- if (_char == '-') {
- if (_stream->readByte() == '-') {
-
- if (_stream->readByte() != '>')
- return parserError("Malformed comment (double-hyphen inside comment body).");
-
- _char = _stream->readByte();
- return true;
- }
- }
-
- _char = _stream->readByte();
- }
-
- return parserError("Comment has no closure.");
- }
-
- return false;
- }
+ bool skipComments();
/**
* Check if a given character can be part of a KEY or VALUE name.
@@ -364,18 +321,8 @@ protected:
/**
* Parses a the first textual token found.
- * There's no reason to overload this.
*/
- bool parseToken() {
- _token.clear();
-
- while (isValidNameChar(_char)) {
- _token += _char;
- _char = _stream->readByte();
- }
-
- return isspace(_char) != 0 || _char == '>' || _char == '=' || _char == '/';
- }
+ bool parseToken();
/**
* Parses the values inside an integer key.
@@ -395,32 +342,9 @@ protected:
* by reference.
* @returns True if the parsing succeeded.
*/
- bool parseIntegerKey(const char *key, int count, ...) {
- char *parseEnd;
- int *num_ptr;
-
- va_list args;
- va_start(args, count);
-
- while (count--) {
- while (isspace(*key))
- key++;
-
- num_ptr = va_arg(args, int*);
- *num_ptr = strtol(key, &parseEnd, 10);
-
- key = parseEnd;
-
- while (isspace(*key))
- key++;
-
- if (count && *key++ != ',')
- return false;
- }
-
- va_end(args);
- return (*key == 0);
- }
+ bool parseIntegerKey(const char *key, int count, ...);
+ bool parseIntegerKey(const Common::String &keyStr, int count, ...);
+ bool vparseIntegerKey(const char *key, int count, va_list args);
bool parseXMLHeader(ParserNode *node);
@@ -446,6 +370,6 @@ private:
Common::Stack<ParserNode*> _activeKey; /** Node stack of the parsed keys */
};
-}
+} // End of namespace Common
#endif
diff --git a/configure b/configure
index a341a87137..bce4e65933 100755
--- a/configure
+++ b/configure
@@ -91,6 +91,7 @@ add_engine groovie2 "Groovie 2 games" no
add_engine hugo "Hugo Trilogy" no
add_engine kyra "Legend of Kyrandia" yes "lol"
add_engine lol "Lands of Lore" no
+add_engine lastexpress "The Last Express" no
add_engine lure "Lure of the Temptress" yes
add_engine m4 "M4/MADS" no
add_engine made "MADE" yes
@@ -105,8 +106,11 @@ add_engine sci32 "SCI32 games" no
add_engine sky "Beneath a Steel Sky" yes
add_engine sword1 "Broken Sword" yes
add_engine sword2 "Broken Sword II" yes
+add_engine sword25 "Broken Sword 2.5" no
add_engine teenagent "Teen Agent" yes
+add_engine testbed "TestBed: the Testing framework" no
add_engine tinsel "Tinsel" yes
+add_engine toon "Toonstruck" no
add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
add_engine tucker "Bud Tucker in Double Trouble" yes
@@ -121,8 +125,11 @@ _flac=auto
_mad=auto
_alsa=auto
_seq_midi=auto
+_timidity=auto
_zlib=auto
_mpeg2=no
+_png=auto
+_theoradec=auto
_fluidsynth=auto
_16bit=auto
_readline=auto
@@ -174,15 +181,15 @@ NASM=""
# to make it possible to change e.g. the location of the
# man pages independently of that of the engine data files,
# which are placed inside $datadir/scummvm
-exec_prefix=NONE
prefix=NONE
+exec_prefix=NONE
bindir='${exec_prefix}/bin'
+libdir='${exec_prefix}/lib'
datarootdir='${prefix}/share'
datadir='${datarootdir}/scummvm'
+mandir='${datarootdir}/man'
docdir='${datarootdir}/doc/scummvm'
-libdir='${exec_prefix}/lib'
#localedir='${datarootdir}/locale'
-mandir='${datarootdir}/man'
# For cross compiling
_host=""
@@ -194,12 +201,8 @@ _host_alias=""
_srcdir=`dirname $0`
_port_mk="ports.mk"
-# Determine a tmp file name, using mktemp(1) when available.
-if type mktemp > /dev/null 2>&1 ; then
- TMPO=`mktemp /tmp/scummvm-conf.XXXXXXXXXX`
-else
- TMPO=./scummvm-conf
-fi
+# Use temp files in the build directory
+TMPO=./scummvm-conf
TMPC=${TMPO}.cpp
TMPLOG=config.log
@@ -365,17 +368,11 @@ get_system_exe_extension() {
arm-riscos)
_exeext=",ff8"
;;
- caanoo-linux)
- _exeext=".caanoo"
- ;;
dreamcast | ds | gamecube | n64 | ps2 | psp | wii)
_exeext=".elf"
;;
- gp2x-linux)
- _exeext=".gp2x"
- ;;
- gp2xwiz-linux)
- _exeext=".wiz"
+ gph-linux)
+ _exeext=".gph"
;;
mingw* | *os2-emx | wince)
_exeext=".exe"
@@ -651,9 +648,9 @@ Usage: $0 [OPTIONS]...
Configuration:
-h, --help display this help and exit
- --backend=BACKEND backend to build (caanoo, dc, gp2x, gp2xwiz, iphone,
- linuxmoto, ds, null, ps2, psp, sdl, wii, wince)
- [sdl]
+ --backend=BACKEND backend to build (android, dc, dingux, ds, gp2x, gph,
+ iphone, linuxmoto, maemo, n64, null, openpandora, ps2,
+ psp, samsungtv, sdl, symbian, wii, wince) [sdl]
Installation directories:
--prefix=PREFIX install architecture-independent files in PREFIX
@@ -672,19 +669,30 @@ Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin]
--libdir=DIR object code libraries [EPREFIX/lib]
--datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
- --datadir=DIR read-only architecture-independent data [DATAROOTDIR/scummvm]
+ --datadir=DIR read-only architecture-independent data
+ [DATAROOTDIR/scummvm]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/scummvm]
Special configuration feature:
--host=HOST cross-compile to target HOST (arm-linux, ...)
- special targets: dreamcast for Sega Dreamcast
+ special targets: android for Android
+ caanoo for Caanoo
+ dingux for Dingux
+ dreamcast for Sega Dreamcast
+ ds for Nintendo DS
gamecube for Nintendo GameCube
+ gp2x for GP2X
+ gp2xwiz for GP2X Wiz
iphone for Apple iPhone
linupy for Yopy PDA
- ds for Nintendo DS
+ motoezx for MotoEZX
+ motomagx for MotoMAGX
+ n64 for Nintendo 64
+ openpandora for OpenPandora
ps2 for PlayStation 2
psp for PlayStation Portable
+ samsungtv for Samsung TV
wii for Nintendo Wii
wince for Windows CE
@@ -695,7 +703,8 @@ $engines_help
Optional Features:
--disable-debug disable building with debugging symbols
--enable-Werror treat warnings as errors
- --enable-release enable building in release mode (this activates optimizations)
+ --enable-release enable building in release mode (this activates
+ optimizations)
--enable-profiling enable profiling
--enable-plugins enable the support for dynamic plugins
--default-dynamic make plugins dynamic by default
@@ -705,7 +714,8 @@ Optional Features:
--disable-hq-scalers exclude HQ2x and HQ3x scalers
--disable-translation don't build support for translated messages
--enable-text-console use text console instead of graphical console
- --enable-verbose-build enable regular echoing of commands during build process
+ --enable-verbose-build enable regular echoing of commands during build
+ process
Optional Libraries:
--with-alsa-prefix=DIR Prefix where alsa is installed (optional)
@@ -732,10 +742,18 @@ Optional Libraries:
--disable-indeo3 disable Indeo3 decoder [autodetect]
- --with-fluidsynth-prefix=DIR Prefix where libfluidsynth is installed (optional)
+ --with-png-prefix=DIR Prefix where libpng is installed (optional)
+ --disable-png disable PNG decoder [autodetect]
+
+ --with-theoradec-prefix=DIR Prefix where libtheoradec is installed (optional)
+ --disable-theoradec disable Theora decoder [autodetect]
+
+ --with-fluidsynth-prefix=DIR Prefix where libfluidsynth is
+ installed (optional)
--disable-fluidsynth disable fluidsynth MIDI driver [autodetect]
- --with-sdl-prefix=DIR Prefix where the sdl-config script is installed (optional)
+ --with-sdl-prefix=DIR Prefix where the sdl-config script is
+ installed (optional)
--with-nasm-prefix=DIR Prefix where nasm executable is installed (optional)
--disable-nasm disable assembly language optimizations [autodetect]
@@ -765,8 +783,10 @@ for ac_option in $@; do
--disable-hq-scalers) _build_hq_scalers=no ;;
--enable-alsa) _alsa=yes ;;
--disable-alsa) _alsa=no ;;
- --enable-seq-midi) _seq_midi=yes ;;
- --disable-seq-midi) _seq_midi=no ;;
+ --enable-seq-midi) _seq_midi=yes ;;
+ --disable-seq-midi) _seq_midi=no ;;
+ --enable-timidity) _timidity=yes ;;
+ --disable-timidity) _timidity=no ;;
--enable-vorbis) _vorbis=yes ;;
--disable-vorbis) _vorbis=no ;;
--enable-tremor) _tremor=yes ;;
@@ -782,6 +802,10 @@ for ac_option in $@; do
--enable-mpeg2) _mpeg2=yes ;;
--disable-indeo3) _indeo3=no ;;
--enable-indeo3) _indeo3=yes ;;
+ --disable-png) _png=no ;;
+ --enable-png) _png=yes ;;
+ --disable-theoradec) _theoradec=no ;;
+ --enable-theoradec) _theoradec=yes ;;
--disable-fluidsynth) _fluidsynth=no ;;
--enable-readline) _readline=yes ;;
--disable-readline) _readline=no ;;
@@ -838,6 +862,16 @@ for ac_option in $@; do
MAD_CFLAGS="-I$arg/include"
MAD_LIBS="-L$arg/lib"
;;
+ --with-png-prefix=*)
+ arg=`echo $ac_option | cut -d '=' -f 2`
+ PNG_CFLAGS="-I$arg/include"
+ PNG_LIBS="-L$arg/lib"
+ ;;
+ --with-theoradec-prefix=*)
+ arg=`echo $ac_option | cut -d '=' -f 2`
+ THEORADEC_CFLAGS="-I$arg/include"
+ THEORADEC_LIBS="-L$arg/lib"
+ ;;
--with-zlib-prefix=*)
arg=`echo $ac_option | cut -d '=' -f 2`
ZLIB_CFLAGS="-I$arg/include"
@@ -883,17 +917,17 @@ for ac_option in $@; do
--host=*)
_host=`echo $ac_option | cut -d '=' -f 2`
;;
- --exec-prefix=*)
- exec_prefix=`echo $ac_option | cut -d '=' -f 2`
- ;;
--prefix=*)
prefix=`echo $ac_option | cut -d '=' -f 2`
;;
+ --exec-prefix=*)
+ exec_prefix=`echo $ac_option | cut -d '=' -f 2`
+ ;;
--bindir=*)
bindir=`echo $ac_option | cut -d '=' -f 2`
;;
- --mandir=*)
- mandir=`echo $ac_option | cut -d '=' -f 2`
+ --libdir=*)
+ libdir=`echo $ac_option | cut -d '=' -f 2`
;;
--datarootdir=*)
datarootdir=`echo $ac_option | cut -d '=' -f 2`
@@ -901,8 +935,11 @@ for ac_option in $@; do
--datadir=*)
datadir=`echo $ac_option | cut -d '=' -f 2`
;;
- --libdir=*)
- libdir=`echo $ac_option | cut -d '=' -f 2`
+ --mandir=*)
+ mandir=`echo $ac_option | cut -d '=' -f 2`
+ ;;
+ --docdir=*)
+ docdir=`echo $ac_option | cut -d '=' -f 2`
;;
--enable-all-engines)
engine_enable_all
@@ -937,9 +974,18 @@ arm-riscos)
_host_cpu=arm
;;
caanoo)
- _host_os=caanoo-linux
+ _host_os=gph-linux
_host_cpu=arm
_host_alias=arm-none-linux-gnueabi
+ if test "$_debug_build" = auto; then
+ # If you want to debug on the Caanoo use '--disable-release --enable-debug'
+ _debug_build=no
+ fi
+
+ if test "$_release_build" = auto; then
+ # Enable release build by default.
+ _release_build=yes
+ fi
;;
dingux)
_host_os=linux
@@ -964,14 +1010,32 @@ gamecube)
_host_alias=powerpc-gekko
;;
gp2x)
- _host_os=gp2x-linux
+ _host_os=gph-linux
_host_cpu=arm
_host_alias=arm-open2x-linux
+ if test "$_debug_build" = auto; then
+ # If you want to debug on the GP2X use '--disable-release --enable-debug'
+ _debug_build=no
+ fi
+
+ if test "$_release_build" = auto; then
+ # Enable release build by default.
+ _release_build=yes
+ fi
;;
gp2xwiz)
- _host_os=gp2xwiz-linux
+ _host_os=gph-linux
_host_cpu=arm
_host_alias=arm-open2x-linux
+ if test "$_debug_build" = auto; then
+ # If you want to debug on the GP2XWiz use '--disable-release --enable-debug'
+ _debug_build=no
+ fi
+
+ if test "$_release_build" = auto; then
+ # Enable release build by default.
+ _release_build=yes
+ fi
;;
i586-mingw32msvc)
_host_os=mingw32msvc
@@ -996,7 +1060,7 @@ motomagx)
_host_cpu=arm
_host_alias=arm-linux-gnueabi
;;
-n64)
+n64)
_host_os=n64
_host_cpu=mips
_host_alias=mips64
@@ -1005,6 +1069,20 @@ neuros)
_host_os=linux
_host_cpu=arm
;;
+openpandora)
+ _host_os=linux
+ _host_cpu=arm
+ _host_alias=arm-angstrom-linux-gnueabi
+ if test "$_debug_build" = auto; then
+ # If you want to debug on the OP use '--disable-release --enable-debug'
+ _debug_build=no
+ fi
+
+ if test "$_release_build" = auto; then
+ # Enable release build by default.
+ _release_build=yes
+ fi
+ ;;
ppc-amigaos)
_host_os=amigaos
_host_cpu=ppc
@@ -1100,10 +1178,10 @@ HOSTEXEEXT=$_exeext
# Determine separator used for $PATH
#
case $_host_os in
-os2-emx* )
+os2-emx*)
SEPARATOR=";"
;;
-* )
+*)
SEPARATOR=":"
;;
esac
@@ -1186,12 +1264,12 @@ else
CXX=
for compiler in $compilers; do
if test_compiler $compiler; then
- echo "success testing compiler: $1" >> "$TMPLOG"
+ echo "success testing compiler: $compiler" >> "$TMPLOG"
CXX=$compiler
echo $CXX
break
else
- echo "failure testing compiler: $1" >> "$TMPLOG"
+ echo "failure testing compiler: $compiler" >> "$TMPLOG"
fi
done
fi
@@ -1487,6 +1565,7 @@ case $_host_os in
DEFINES="$DEFINES -D__N64__ -DLIMIT_FPS -DNONSTANDARD_PORT"
DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_COMMAND_LINE"
DEFINES="$DEFINES -DDISABLE_FANCY_THEMES -DDISABLE_DOSBOX_OPL -DDISABLE_SID -DDISABLE_NES_APU"
+ DEFINES="$DEFINES -DREDUCE_MEMORY_USAGE"
;;
os2-emx*)
_unix=yes # FIXME??? Why??
@@ -1565,7 +1644,15 @@ if test -n "$_host"; then
_need_memalign=yes
;;
caanoo)
- DEFINES="$DEFINES -DGP2XWIZ -DCAANOO -DNDEBUG"
+ # This uses the GPH backend.
+ DEFINES="$DEFINES -DGPH_DEVICE"
+ DEFINES="$DEFINES -DCAANOO -DREDUCE_MEMORY_USAGE"
+ if test "$_debug_build" = yes; then
+ DEFINES="$DEFINES -DGPH_DEBUG"
+ else
+ # Use -O3 on the Caanoo for non-debug builds.
+ CXXFLAGS="$CXXFLAGS -O3"
+ fi
CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s"
ASFLAGS="$ASFLAGS"
_unix=yes
@@ -1575,19 +1662,19 @@ if test -n "$_host"; then
add_line_to_config_mk 'USE_ARM_GFX_ASM = 1'
add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1'
add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1'
- _backend="gp2xwiz"
+ _backend="gph"
_build_hq_scalers=no
_mt32emu=no
_vkeybd=yes
_seq_midi=no
- _port_mk="backends/platform/gp2xwiz/caanoo/caanoo-bundle.mk"
- ;;
+ _port_mk="backends/platform/gph/caanoo-bundle.mk"
+ ;;
*darwin*)
_ranlib=$_host-ranlib
_strip=$_host-strip
;;
dingux)
- DEFINES="$DEFINES -DUNIX -DDINGUX -DDISABLE_DOSBOX_OPL"
+ DEFINES="$DEFINES -DUNIX -DDINGUX -DDISABLE_DOSBOX_OPL -DREDUCE_MEMORY_USAGE"
ASFLAGS="$ASFLAGS"
CXXFLAGS="$CXXFLAGS -msoft-float -mips32"
_need_memalign=yes
@@ -1600,6 +1687,7 @@ if test -n "$_host"; then
_vorbis=no
# Force disable seq on dingux, no way to use it and it would get enabled by default with configure
_seq_midi=no
+ _port_mk="backends/platform/dingux/dingux.mk"
;;
dreamcast)
DEFINES="$DEFINES -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE"
@@ -1647,7 +1735,12 @@ if test -n "$_host"; then
add_line_to_config_h "#define USE_WII_DI"
;;
gp2x)
- DEFINES="$DEFINES -DGP2X -DNDEBUG"
+ # This uses the GPH backend.
+ DEFINES="$DEFINES -DGPH_DEVICE"
+ DEFINES="$DEFINES -DGP2X -DREDUCE_MEMORY_USAGE"
+ if test "$_debug_build" = yes; then
+ DEFINES="$DEFINES -DGPH_DEBUG"
+ fi
CXXFLAGS="$CXXFLAGS -march=armv4t"
ASFLAGS="$ASFLAGS -mfloat-abi=soft"
LDFLAGS="$LDFLAGS -static"
@@ -1666,7 +1759,12 @@ if test -n "$_host"; then
_port_mk="backends/platform/gp2x/gp2x-bundle.mk"
;;
gp2xwiz)
- DEFINES="$DEFINES -DGP2XWIZ -DNDEBUG"
+ # This uses the GPH backend.
+ DEFINES="$DEFINES -DGPH_DEVICE"
+ DEFINES="$DEFINES -DGP2XWIZ -DREDUCE_MEMORY_USAGE"
+ if test "$_debug_build" = yes; then
+ DEFINES="$DEFINES -DGPH_DEBUG"
+ fi
CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s"
ASFLAGS="$ASFLAGS -mfloat-abi=soft"
_unix=yes
@@ -1676,12 +1774,12 @@ if test -n "$_host"; then
add_line_to_config_mk 'USE_ARM_GFX_ASM = 1'
add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1'
add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1'
- _backend="gp2xwiz"
+ _backend="gph"
_build_hq_scalers=no
_mt32emu=no
_vkeybd=yes
_seq_midi=no
- _port_mk="backends/platform/gp2xwiz/gp2xwiz-bundle.mk"
+ _port_mk="backends/platform/gph/gp2xwiz-bundle.mk"
;;
iphone)
DEFINES="$DEFINES -DIPHONE"
@@ -1709,6 +1807,8 @@ if test -n "$_host"; then
_ranlib=$_host-ranlib
;;
mips-sgi*)
+ LDFLAGS="$LDFLAGS -static-libgcc"
+ LIBS="$LIBS -laudio"
_endian=big
_need_memalign=yes
;;
@@ -1767,6 +1867,7 @@ if test -n "$_host"; then
_mad=yes
_tremor=yes
_zlib=yes
+ _port_mk="backends/platform/n64/n64.mk"
;;
neuros)
DEFINES="$DEFINES -DNEUROS"
@@ -1776,6 +1877,30 @@ if test -n "$_host"; then
_build_hq_scalers=no
_mt32emu=no
;;
+ openpandora)
+ DEFINES="$DEFINES -DOPENPANDORA -DREDUCE_MEMORY_USAGE"
+ if test "$_release_build" = no; then
+ DEFINES="$DEFINES -DOP_DEBUG"
+ else
+ # Use -O3 on the OpenPandora for non-debug builds.
+ CXXFLAGS="$CXXFLAGS -O3"
+ fi
+ CXXFLAGS="$CXXFLAGS -march=armv7-a -mtune=cortex-a8 -mfpu=neon"
+ ASFLAGS="$ASFLAGS -mfloat-abi=soft"
+ _unix=yes
+ _need_memalign=yes
+ add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_GFX_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1'
+ add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1'
+ _backend="openpandora"
+ _build_hq_scalers=yes
+ _mt32emu=no
+ _vkeybd=no
+ _seq_midi=no
+ _port_mk="backends/platform/openpandora/op-bundle.mk"
+ ;;
ppc-amigaos)
_endian=big
_need_memalign=yes
@@ -1918,7 +2043,7 @@ fi
# Enable 16bit support only for backends which support it
#
case $_backend in
- dreamcast | dingux | samsungtv | sdl | wii | psp)
+ dingux | dreamcast | gph | openpandora | psp | samsungtv | sdl | wii)
if test "$_16bit" = auto ; then
_16bit=yes
else
@@ -2038,6 +2163,24 @@ _mak_plugins='
PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/wii/plugin.ld
'
;;
+ gph*)
+_def_plugin='
+#define PLUGIN_PREFIX ""
+#define PLUGIN_SUFFIX ".plugin"
+'
+_mak_plugins='
+DYNAMIC_MODULES := 1
+PLUGIN_PREFIX :=
+PLUGIN_SUFFIX := .plugin
+PLUGIN_EXTRA_DEPS = $(EXECUTABLE)
+CXXFLAGS += -DDYNAMIC_MODULES
+CXXFLAGS += -fpic
+PLUGIN_LDFLAGS += -shared
+PRE_OBJS_FLAGS := -Wl,-export-dynamic -Wl,-whole-archive
+POST_OBJS_FLAGS := -Wl,-no-whole-archive
+LIBS += -ldl
+'
+ ;;
linux*|android)
_def_plugin='
#define PLUGIN_PREFIX "lib"
@@ -2085,7 +2228,7 @@ PLUGIN_LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -Wl,-T$(srcdir)/backend
DEFINES="$DEFINES -DMIPS_TARGET -DELF_LOADER_CXA_ATEXIT"
_mak_plugins='
LDFLAGS += -Wl,-T$(srcdir)/backends/platform/psp/main_prog.ld
-PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/plugin.ld -lc -Wl,--wrap,memcpy
+PLUGIN_LDFLAGS += -Wl,-T$(srcdir)/backends/plugins/psp/plugin.ld -lc
'
;;
*)
@@ -2275,6 +2418,62 @@ define_in_config_h_if_yes "$_alsa" 'USE_ALSA'
echo "$_alsa"
#
+# Check for PNG
+#
+echocheck "PNG >= 1.2.8"
+if test "$_png" = auto ; then
+ _png=no
+ cat > $TMPC << EOF
+#include <png.h>
+int main(void) {
+#if PNG_LIBPNG_VER >= 10208
+#else
+ syntax error
+#endif
+ return 0;
+}
+EOF
+ cc_check $PNG_CFLAGS $PNG_LIBS -lpng && _png=yes
+fi
+if test "$_png" = yes ; then
+ LIBS="$LIBS $PNG_LIBS -lpng"
+ INCLUDES="$INCLUDES $PNG_CFLAGS"
+fi
+define_in_config_if_yes "$_png" 'USE_PNG'
+echo "$_png"
+
+if test `get_engine_build sword25` = yes && test ! "$_png" = yes ; then
+ echo "...disabling Broken Sword 2.5 engine. PNG is required"
+ engine_disable sword25
+fi
+
+#
+# Check for Theora Decoder
+#
+echocheck "libtheoradec >= 1.0"
+if test "$_vorbis" = no ; then
+ echo "skipping. no vorbis"
+ _theoradec=notsupported
+fi
+if test "$_theoradec" = auto ; then
+ _theoradec=no
+ cat > $TMPC << EOF
+#include <theora/theoradec.h>
+#include <vorbis/codec.h>
+int main(void) { th_ycbcr_buffer yuv; th_decode_ycbcr_out(NULL, yuv); }
+EOF
+ cc_check $THEORADEC_CFLAGS $THEORADEC_LIBS -ltheoradec && _theoradec=yes
+fi
+if test "$_theoradec" = yes ; then
+ LIBS="$LIBS $THEORADEC_LIBS -ltheoradec"
+ INCLUDES="$INCLUDES $THEORADEC_CFLAGS"
+fi
+define_in_config_if_yes "$_theoradec" 'USE_THEORADEC'
+if test ! "$_theoradec" = notsupported ; then
+ echo "$_theoradec"
+fi
+
+#
# Check for SEQ MIDI
#
echocheck "SEQ MIDI"
@@ -2288,6 +2487,19 @@ define_in_config_h_if_yes "$_seq_midi" 'USE_SEQ_MIDI'
echo "$_seq_midi"
#
+# Check for TiMidity(++)
+#
+echocheck "TiMidity"
+if test "$_timidity" = auto ; then
+ # TODO: Is there a good possibility of auto detecting whether we
+ # should include TiMidity support? It can only be used on Unix
+ # currently so we use that as "detection" for now.
+ _timidity="$_unix"
+fi
+define_in_config_h_if_yes "$_timidity" 'USE_TIMIDITY'
+echo "$_timidity"
+
+#
# Check for ZLib
#
echocheck "zlib"
@@ -2452,14 +2664,14 @@ if test "$_have_x86" = yes ; then
_nasm=no
else
case $_host_os in
- os2-emx*)
- NASMFLAGS="$NASMFLAGS -f aout"
+ darwin*)
+ NASMFLAGS="$NASMFLAGS -f macho"
;;
mingw*)
NASMFLAGS="$NASMFLAGS -f win32"
;;
- darwin*)
- NASMFLAGS="$NASMFLAGS -f macho"
+ os2-emx*)
+ NASMFLAGS="$NASMFLAGS -f aout"
;;
*)
NASMFLAGS="$NASMFLAGS -f elf"
@@ -2513,8 +2725,16 @@ test "x$prefix" = xNONE && prefix=/usr/local
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DEFINES="$DEFINES -DDATA_PATH=\\\"$datadir\\\""
-DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"$libdir/scummvm\\\""
+case $_backend in
+ openpandora)
+ # Add ../plugins as a path so plugins can be found when running from a .PND.
+ DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"../plugins\\\""
+ ;;
+ *)
+ DEFINES="$DEFINES -DPLUGIN_DIRECTORY=\\\"$libdir/scummvm\\\""
+ ;;
+esac
#
# Set variables for profiling.
@@ -2603,13 +2823,6 @@ case $_backend in
add_line_to_config_mk 'PLUGIN_LDFLAGS += -Lbuild.tmp -lscummvm'
add_line_to_config_mk 'PLUGIN_EXTRA_DEPS += build.tmp/libscummvm.so'
;;
- caanoo)
- find_sdlconfig
- INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`"
- LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`"
- LDFLAGS="$LDFLAGS"
- CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s"
- ;;
dc)
INCLUDES="$INCLUDES "'-I$(srcdir)/backends/platform/dc -isystem $(ronindir)/include'
LDFLAGS="$LDFLAGS -Wl,-Ttext,0x8c010000 -nostartfiles "'$(ronindir)/lib/crt0.o -L$(ronindir)/lib'
@@ -2633,15 +2846,13 @@ case $_backend in
find_sdlconfig
INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`"
LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`"
- LDFLAGS="$LDFLAGS -static"
- CXXFLAGS="$CXXFLAGS -march=armv4t"
+ LDFLAGS="$LDFLAGS"
;;
- gp2xwiz)
+ gph)
find_sdlconfig
INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`"
LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`"
LDFLAGS="$LDFLAGS"
- CXXFLAGS="$CXXFLAGS -mcpu=arm926ej-s -mtune=arm926ej-s"
;;
iphone)
OBJCFLAGS="$OBJCFLAGS --std=c99"
@@ -2663,6 +2874,12 @@ case $_backend in
null)
DEFINES="$DEFINES -DUSE_NULL_DRIVER"
;;
+ openpandora)
+ find_sdlconfig
+ INCLUDES="$INCLUDES `$_sdlconfig --prefix="$_sdlpath" --cflags`"
+ LIBS="$LIBS `$_sdlconfig --prefix="$_sdlpath" --libs`"
+ LDFLAGS="$LDFLAGS"
+ ;;
ps2)
# TODO ps2
DEFINES="$DEFINES -D_EE -DFORCE_RTL"
@@ -2674,9 +2891,8 @@ case $_backend in
LIBS="$LIBS -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lm -lc -lfileXio -lkernel -lstdc++ "
;;
psp)
- DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL"
+ DEFINES="$DEFINES -D__PSP__ -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL"
LIBS="$LIBS -lpng -Wl,-Map,mapfile.txt"
- LDFLAGS="$LDFLAGS -Wl,--wrap,memcpy"
;;
samsungtv)
find_sdlconfig
@@ -2722,7 +2938,7 @@ if test "$have_gcc" = yes ; then
case $_host_os in
# newlib-based system include files suppress non-C89 function
# declarations under __STRICT_ANSI__
- amigaos* | android | ds | dreamcast | gamecube | mingw* | n64 | psp | wii | wince )
+ amigaos* | android | dreamcast | ds | gamecube | mingw* | n64 | psp | wii | wince )
CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter"
;;
*)
@@ -2886,11 +3102,11 @@ NASMFLAGS := $NASMFLAGS
prefix = $prefix
exec_prefix = $exec_prefix
bindir = $bindir
+libdir = $libdir
datarootdir = $datarootdir
datadir = $datadir
-docdir = $docdir
-libdir = $libdir
mandir = $mandir
+docdir = $docdir
$_config_mk_data
diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml
index 55e3bf2f38..3b69c9f1f3 100644
--- a/dists/android/AndroidManifest.xml
+++ b/dists/android/AndroidManifest.xml
@@ -2,11 +2,11 @@
<!-- NB: android:versionCode needs to be bumped for formal releases -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.inodes.gus.scummvm"
- android:versionCode="6" android:versionName="1.2.0svn"
+ android:versionCode="6" android:versionName="1.3.0svn"
android:installLocation="preferExternal">
- <!-- This version is built against a cupcake (and newer?) ABI.
- It works on Android 1.5 (SDK 3) and newer.
+ <!-- This version works on Android 1.5 (SDK 3) and newer, but we
+ want Android 2.2 (SDK 8) defaults and features.
-->
<uses-sdk android:minSdkVersion="3"
android:targetSdkVersion="8" />
diff --git a/dists/engine-data/create-testbed-data.sh b/dists/engine-data/create-testbed-data.sh
new file mode 100755
index 0000000000..754ec4d683
--- /dev/null
+++ b/dists/engine-data/create-testbed-data.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+# Create the directory structure
+# Avoided bash shortcuts / file-seperators in interest of portability
+
+if [ -e testbed ]; then
+ echo "Game-data already present as testbed/"
+ echo "To regenerate, remove and rerun"
+ exit 0
+fi
+
+mkdir testbed
+
+cd testbed
+
+# For game detection
+echo "ScummVM rocks!" > TESTBED
+
+mkdir test1
+mkdir Test2
+mkdir TEST3
+mkdir tEST4
+mkdir test5
+
+
+cd test1
+echo "It works!" > file.txt
+cd ..
+
+cd Test2
+echo "It works!" > File.txt
+cd ..
+
+cd TEST3
+echo "It works!" > FILE.txt
+cd ..
+
+cd tEST4
+echo "It works!" > fILe.txt
+cd ..
+
+cd test5
+echo "It works!" > file.
+cd ..
+
+# back to the top
+cd ..
+
+# move the audiocd data to newly created directory
+cp -r testbed-audiocd-files testbed/audiocd-files
+mv testbed/audiocd-files/music.mid testbed/
+echo "Game data created"
diff --git a/dists/engine-data/drascula.dat b/dists/engine-data/drascula.dat
index 646da84aea..feecda80a2 100644
--- a/dists/engine-data/drascula.dat
+++ b/dists/engine-data/drascula.dat
Binary files differ
diff --git a/dists/engine-data/hugo.dat b/dists/engine-data/hugo.dat
index 348339bdda..8fda3fb2f4 100644
--- a/dists/engine-data/hugo.dat
+++ b/dists/engine-data/hugo.dat
Binary files differ
diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat
index d8cb28076a..23e866c62e 100644
--- a/dists/engine-data/kyra.dat
+++ b/dists/engine-data/kyra.dat
Binary files differ
diff --git a/dists/engine-data/testbed-audiocd-files/music.mid b/dists/engine-data/testbed-audiocd-files/music.mid
new file mode 100644
index 0000000000..35e0f77073
--- /dev/null
+++ b/dists/engine-data/testbed-audiocd-files/music.mid
Binary files differ
diff --git a/dists/engine-data/testbed-audiocd-files/track01.mp3 b/dists/engine-data/testbed-audiocd-files/track01.mp3
new file mode 100644
index 0000000000..53d057ee96
--- /dev/null
+++ b/dists/engine-data/testbed-audiocd-files/track01.mp3
Binary files differ
diff --git a/dists/engine-data/testbed-audiocd-files/track02.mp3 b/dists/engine-data/testbed-audiocd-files/track02.mp3
new file mode 100644
index 0000000000..daf8e4860d
--- /dev/null
+++ b/dists/engine-data/testbed-audiocd-files/track02.mp3
Binary files differ
diff --git a/dists/engine-data/testbed-audiocd-files/track03.mp3 b/dists/engine-data/testbed-audiocd-files/track03.mp3
new file mode 100644
index 0000000000..1ef385d640
--- /dev/null
+++ b/dists/engine-data/testbed-audiocd-files/track03.mp3
Binary files differ
diff --git a/dists/engine-data/testbed-audiocd-files/track04.mp3 b/dists/engine-data/testbed-audiocd-files/track04.mp3
new file mode 100644
index 0000000000..7607087f08
--- /dev/null
+++ b/dists/engine-data/testbed-audiocd-files/track04.mp3
Binary files differ
diff --git a/dists/engine-data/toon.dat b/dists/engine-data/toon.dat
new file mode 100644
index 0000000000..82354c4239
--- /dev/null
+++ b/dists/engine-data/toon.dat
Binary files differ
diff --git a/dists/iphone/Info.plist b/dists/iphone/Info.plist
index a042504b9f..9fc4debc92 100644
--- a/dists/iphone/Info.plist
+++ b/dists/iphone/Info.plist
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
@@ -15,19 +15,18 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.0.0</string>
+ <string>1.3.0svn</string>
<key>CFBundleSignature</key>
<string>????</string>
- <key>CFBundleShortVersionString</key>
- <string>1.2.0svn</string>
<key>CFBundleVersion</key>
- <string>svn</string>
+ <string>1.3.0svn</string>
<key>CFBundleIconFile</key>
<string>icon.png</string>
<key>CFBundleIconFiles</key>
<array>
<string>icon.png</string>
<string>icon-72.png</string>
+ <string>icon4.png</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
diff --git a/dists/iphone/Info.plist.in b/dists/iphone/Info.plist.in
index 7792857f40..efb93832e7 100644
--- a/dists/iphone/Info.plist.in
+++ b/dists/iphone/Info.plist.in
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
@@ -15,15 +15,19 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.0.0</string>
+ <string>@VERSION@</string>
<key>CFBundleSignature</key>
<string>????</string>
- <key>CFBundleShortVersionString</key>
- <string>@VERSION@</string>
<key>CFBundleVersion</key>
- <string>svn</string>
+ <string>@VERSION@</string>
<key>CFBundleIconFile</key>
<string>icon.png</string>
+ <key>CFBundleIconFiles</key>
+ <array>
+ <string>icon.png</string>
+ <string>icon-72.png</string>
+ <string>icon4.png</string>
+ </array>
<key>UIPrerenderedIcon</key>
<true/>
<key>UIDeviceFamily</key>
diff --git a/dists/iphone/icon4.png b/dists/iphone/icon4.png
new file mode 100644
index 0000000000..5abe16ed51
--- /dev/null
+++ b/dists/iphone/icon4.png
Binary files differ
diff --git a/dists/iphone/scummvm.xcodeproj/project.pbxproj b/dists/iphone/scummvm.xcodeproj/project.pbxproj
index 5512318918..a2c28f8c66 100755
--- a/dists/iphone/scummvm.xcodeproj/project.pbxproj
+++ b/dists/iphone/scummvm.xcodeproj/project.pbxproj
@@ -7,6 +7,189 @@
objects = {
/* Begin PBXBuildFile section */
+ 8CB5A9C11253FD6900CB6BC7 /* m4_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B71253FD6800CB6BC7 /* m4_scene.cpp */; };
+ 8CB5A9C21253FD6900CB6BC7 /* mads_logic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B91253FD6800CB6BC7 /* mads_logic.cpp */; };
+ 8CB5A9C31253FD6900CB6BC7 /* mads_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BB1253FD6800CB6BC7 /* mads_player.cpp */; };
+ 8CB5A9C41253FD6900CB6BC7 /* mads_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BD1253FD6800CB6BC7 /* mads_scene.cpp */; };
+ 8CB5A9C51253FD6900CB6BC7 /* mads_views.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BF1253FD6900CB6BC7 /* mads_views.cpp */; };
+ 8CB5A9C61253FD6900CB6BC7 /* m4_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B71253FD6800CB6BC7 /* m4_scene.cpp */; };
+ 8CB5A9C71253FD6900CB6BC7 /* mads_logic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B91253FD6800CB6BC7 /* mads_logic.cpp */; };
+ 8CB5A9C81253FD6900CB6BC7 /* mads_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BB1253FD6800CB6BC7 /* mads_player.cpp */; };
+ 8CB5A9C91253FD6900CB6BC7 /* mads_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BD1253FD6800CB6BC7 /* mads_scene.cpp */; };
+ 8CB5A9CA1253FD6900CB6BC7 /* mads_views.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BF1253FD6900CB6BC7 /* mads_views.cpp */; };
+ 8CB5A9CB1253FD6900CB6BC7 /* m4_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B71253FD6800CB6BC7 /* m4_scene.cpp */; };
+ 8CB5A9CC1253FD6900CB6BC7 /* mads_logic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9B91253FD6800CB6BC7 /* mads_logic.cpp */; };
+ 8CB5A9CD1253FD6900CB6BC7 /* mads_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BB1253FD6800CB6BC7 /* mads_player.cpp */; };
+ 8CB5A9CE1253FD6900CB6BC7 /* mads_scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BD1253FD6800CB6BC7 /* mads_scene.cpp */; };
+ 8CB5A9CF1253FD6900CB6BC7 /* mads_views.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CB5A9BF1253FD6900CB6BC7 /* mads_views.cpp */; };
+ 8CB5A9D91253FDF500CB6BC7 /* drascula.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D51253FDF400CB6BC7 /* drascula.dat */; };
+ 8CB5A9DA1253FDF500CB6BC7 /* hugo.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D61253FDF500CB6BC7 /* hugo.dat */; };
+ 8CB5A9DB1253FDF500CB6BC7 /* m4.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D71253FDF500CB6BC7 /* m4.dat */; };
+ 8CB5A9DC1253FDF500CB6BC7 /* teenagent.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D81253FDF500CB6BC7 /* teenagent.dat */; };
+ 8CB5A9DD1253FDF500CB6BC7 /* drascula.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D51253FDF400CB6BC7 /* drascula.dat */; };
+ 8CB5A9DE1253FDF500CB6BC7 /* hugo.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D61253FDF500CB6BC7 /* hugo.dat */; };
+ 8CB5A9DF1253FDF500CB6BC7 /* m4.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D71253FDF500CB6BC7 /* m4.dat */; };
+ 8CB5A9E01253FDF500CB6BC7 /* teenagent.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D81253FDF500CB6BC7 /* teenagent.dat */; };
+ 8CB5A9E11253FDF500CB6BC7 /* drascula.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D51253FDF400CB6BC7 /* drascula.dat */; };
+ 8CB5A9E21253FDF500CB6BC7 /* hugo.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D61253FDF500CB6BC7 /* hugo.dat */; };
+ 8CB5A9E31253FDF500CB6BC7 /* m4.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D71253FDF500CB6BC7 /* m4.dat */; };
+ 8CB5A9E41253FDF500CB6BC7 /* teenagent.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CB5A9D81253FDF500CB6BC7 /* teenagent.dat */; };
+ 8CD1ED0B126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC6126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED0C126202AB00FA198C /* display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC7126202AA00FA198C /* display.cpp */; };
+ 8CD1ED0D126202AB00FA198C /* engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC9126202AA00FA198C /* engine.cpp */; };
+ 8CD1ED0E126202AB00FA198C /* file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCB126202AA00FA198C /* file.cpp */; };
+ 8CD1ED0F126202AB00FA198C /* hugo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCF126202AA00FA198C /* hugo.cpp */; };
+ 8CD1ED10126202AB00FA198C /* intro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD1126202AA00FA198C /* intro.cpp */; };
+ 8CD1ED11126202AB00FA198C /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD3126202AA00FA198C /* inventory.cpp */; };
+ 8CD1ED14126202AB00FA198C /* mouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD7126202AA00FA198C /* mouse.cpp */; };
+ 8CD1ED15126202AB00FA198C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD9126202AA00FA198C /* parser.cpp */; };
+ 8CD1ED16126202AB00FA198C /* route.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDB126202AA00FA198C /* route.cpp */; };
+ 8CD1ED17126202AB00FA198C /* schedule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDD126202AA00FA198C /* schedule.cpp */; };
+ 8CD1ED18126202AB00FA198C /* sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDF126202AA00FA198C /* sound.cpp */; };
+ 8CD1ED19126202AB00FA198C /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE1126202AA00FA198C /* util.cpp */; };
+ 8CD1ED1A126202AB00FA198C /* anim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE4126202AA00FA198C /* anim.cpp */; };
+ 8CD1ED1B126202AB00FA198C /* audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE6126202AA00FA198C /* audio.cpp */; };
+ 8CD1ED1C126202AB00FA198C /* character.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE8126202AA00FA198C /* character.cpp */; };
+ 8CD1ED1D126202AB00FA198C /* conversation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEA126202AA00FA198C /* conversation.cpp */; };
+ 8CD1ED1E126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEC126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED1F126202AB00FA198C /* drew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECED126202AA00FA198C /* drew.cpp */; };
+ 8CD1ED20126202AB00FA198C /* flux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEF126202AA00FA198C /* flux.cpp */; };
+ 8CD1ED21126202AB00FA198C /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF1126202AA00FA198C /* font.cpp */; };
+ 8CD1ED22126202AB00FA198C /* hotspot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF3126202AA00FA198C /* hotspot.cpp */; };
+ 8CD1ED25126202AB00FA198C /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF7126202AA00FA198C /* movie.cpp */; };
+ 8CD1ED26126202AB00FA198C /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF9126202AA00FA198C /* path.cpp */; };
+ 8CD1ED27126202AB00FA198C /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFB126202AA00FA198C /* picture.cpp */; };
+ 8CD1ED28126202AB00FA198C /* resource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFD126202AA00FA198C /* resource.cpp */; };
+ 8CD1ED29126202AB00FA198C /* script.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFF126202AA00FA198C /* script.cpp */; };
+ 8CD1ED2A126202AB00FA198C /* script_func.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED01126202AA00FA198C /* script_func.cpp */; };
+ 8CD1ED2B126202AB00FA198C /* state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED03126202AA00FA198C /* state.cpp */; };
+ 8CD1ED2C126202AB00FA198C /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED05126202AA00FA198C /* text.cpp */; };
+ 8CD1ED2D126202AB00FA198C /* tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED07126202AA00FA198C /* tools.cpp */; };
+ 8CD1ED2E126202AB00FA198C /* toon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED09126202AA00FA198C /* toon.cpp */; };
+ 8CD1ED2F126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC6126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED30126202AB00FA198C /* display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC7126202AA00FA198C /* display.cpp */; };
+ 8CD1ED31126202AB00FA198C /* engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC9126202AA00FA198C /* engine.cpp */; };
+ 8CD1ED32126202AB00FA198C /* file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCB126202AA00FA198C /* file.cpp */; };
+ 8CD1ED33126202AB00FA198C /* hugo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCF126202AA00FA198C /* hugo.cpp */; };
+ 8CD1ED34126202AB00FA198C /* intro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD1126202AA00FA198C /* intro.cpp */; };
+ 8CD1ED35126202AB00FA198C /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD3126202AA00FA198C /* inventory.cpp */; };
+ 8CD1ED38126202AB00FA198C /* mouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD7126202AA00FA198C /* mouse.cpp */; };
+ 8CD1ED39126202AB00FA198C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD9126202AA00FA198C /* parser.cpp */; };
+ 8CD1ED3A126202AB00FA198C /* route.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDB126202AA00FA198C /* route.cpp */; };
+ 8CD1ED3B126202AB00FA198C /* schedule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDD126202AA00FA198C /* schedule.cpp */; };
+ 8CD1ED3C126202AB00FA198C /* sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDF126202AA00FA198C /* sound.cpp */; };
+ 8CD1ED3D126202AB00FA198C /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE1126202AA00FA198C /* util.cpp */; };
+ 8CD1ED3E126202AB00FA198C /* anim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE4126202AA00FA198C /* anim.cpp */; };
+ 8CD1ED3F126202AB00FA198C /* audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE6126202AA00FA198C /* audio.cpp */; };
+ 8CD1ED40126202AB00FA198C /* character.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE8126202AA00FA198C /* character.cpp */; };
+ 8CD1ED41126202AB00FA198C /* conversation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEA126202AA00FA198C /* conversation.cpp */; };
+ 8CD1ED42126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEC126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED43126202AB00FA198C /* drew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECED126202AA00FA198C /* drew.cpp */; };
+ 8CD1ED44126202AB00FA198C /* flux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEF126202AA00FA198C /* flux.cpp */; };
+ 8CD1ED45126202AB00FA198C /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF1126202AA00FA198C /* font.cpp */; };
+ 8CD1ED46126202AB00FA198C /* hotspot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF3126202AA00FA198C /* hotspot.cpp */; };
+ 8CD1ED49126202AB00FA198C /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF7126202AA00FA198C /* movie.cpp */; };
+ 8CD1ED4A126202AB00FA198C /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF9126202AA00FA198C /* path.cpp */; };
+ 8CD1ED4B126202AB00FA198C /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFB126202AA00FA198C /* picture.cpp */; };
+ 8CD1ED4C126202AB00FA198C /* resource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFD126202AA00FA198C /* resource.cpp */; };
+ 8CD1ED4D126202AB00FA198C /* script.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFF126202AA00FA198C /* script.cpp */; };
+ 8CD1ED4E126202AB00FA198C /* script_func.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED01126202AA00FA198C /* script_func.cpp */; };
+ 8CD1ED4F126202AB00FA198C /* state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED03126202AA00FA198C /* state.cpp */; };
+ 8CD1ED50126202AB00FA198C /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED05126202AA00FA198C /* text.cpp */; };
+ 8CD1ED51126202AB00FA198C /* tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED07126202AA00FA198C /* tools.cpp */; };
+ 8CD1ED52126202AB00FA198C /* toon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED09126202AA00FA198C /* toon.cpp */; };
+ 8CD1ED53126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC6126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED54126202AB00FA198C /* display.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC7126202AA00FA198C /* display.cpp */; };
+ 8CD1ED55126202AB00FA198C /* engine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECC9126202AA00FA198C /* engine.cpp */; };
+ 8CD1ED56126202AB00FA198C /* file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCB126202AA00FA198C /* file.cpp */; };
+ 8CD1ED57126202AB00FA198C /* hugo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECCF126202AA00FA198C /* hugo.cpp */; };
+ 8CD1ED58126202AB00FA198C /* intro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD1126202AA00FA198C /* intro.cpp */; };
+ 8CD1ED59126202AB00FA198C /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD3126202AA00FA198C /* inventory.cpp */; };
+ 8CD1ED5C126202AB00FA198C /* mouse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD7126202AA00FA198C /* mouse.cpp */; };
+ 8CD1ED5D126202AB00FA198C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECD9126202AA00FA198C /* parser.cpp */; };
+ 8CD1ED5E126202AB00FA198C /* route.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDB126202AA00FA198C /* route.cpp */; };
+ 8CD1ED5F126202AB00FA198C /* schedule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDD126202AA00FA198C /* schedule.cpp */; };
+ 8CD1ED60126202AB00FA198C /* sound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECDF126202AA00FA198C /* sound.cpp */; };
+ 8CD1ED61126202AB00FA198C /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE1126202AA00FA198C /* util.cpp */; };
+ 8CD1ED62126202AB00FA198C /* anim.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE4126202AA00FA198C /* anim.cpp */; };
+ 8CD1ED63126202AB00FA198C /* audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE6126202AA00FA198C /* audio.cpp */; };
+ 8CD1ED64126202AB00FA198C /* character.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECE8126202AA00FA198C /* character.cpp */; };
+ 8CD1ED65126202AB00FA198C /* conversation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEA126202AA00FA198C /* conversation.cpp */; };
+ 8CD1ED66126202AB00FA198C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEC126202AA00FA198C /* detection.cpp */; };
+ 8CD1ED67126202AB00FA198C /* drew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECED126202AA00FA198C /* drew.cpp */; };
+ 8CD1ED68126202AB00FA198C /* flux.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECEF126202AA00FA198C /* flux.cpp */; };
+ 8CD1ED69126202AB00FA198C /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF1126202AA00FA198C /* font.cpp */; };
+ 8CD1ED6A126202AB00FA198C /* hotspot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF3126202AA00FA198C /* hotspot.cpp */; };
+ 8CD1ED6D126202AB00FA198C /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF7126202AA00FA198C /* movie.cpp */; };
+ 8CD1ED6E126202AB00FA198C /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECF9126202AA00FA198C /* path.cpp */; };
+ 8CD1ED6F126202AB00FA198C /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFB126202AA00FA198C /* picture.cpp */; };
+ 8CD1ED70126202AB00FA198C /* resource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFD126202AA00FA198C /* resource.cpp */; };
+ 8CD1ED71126202AB00FA198C /* script.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ECFF126202AA00FA198C /* script.cpp */; };
+ 8CD1ED72126202AB00FA198C /* script_func.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED01126202AA00FA198C /* script_func.cpp */; };
+ 8CD1ED73126202AB00FA198C /* state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED03126202AA00FA198C /* state.cpp */; };
+ 8CD1ED74126202AB00FA198C /* text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED05126202AA00FA198C /* text.cpp */; };
+ 8CD1ED75126202AB00FA198C /* tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED07126202AA00FA198C /* tools.cpp */; };
+ 8CD1ED76126202AB00FA198C /* toon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD1ED09126202AA00FA198C /* toon.cpp */; };
+ 8CD1ED881262030100FA198C /* toon.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CD1ED871262030100FA198C /* toon.dat */; };
+ 8CD1ED891262030100FA198C /* toon.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CD1ED871262030100FA198C /* toon.dat */; };
+ 8CD1ED8A1262030100FA198C /* toon.dat in Resources */ = {isa = PBXBuildFile; fileRef = 8CD1ED871262030100FA198C /* toon.dat */; };
+ 8CD80C8B126271A9001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C89126271A9001C6C87 /* surface.cpp */; };
+ 8CD80C8C126271A9001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C89126271A9001C6C87 /* surface.cpp */; };
+ 8CD80C8D126271A9001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C89126271A9001C6C87 /* surface.cpp */; };
+ 8CD80C91126271BD001C6C87 /* gfx_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C90126271BD001C6C87 /* gfx_towns.cpp */; };
+ 8CD80C92126271BD001C6C87 /* gfx_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C90126271BD001C6C87 /* gfx_towns.cpp */; };
+ 8CD80C93126271BD001C6C87 /* gfx_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80C90126271BD001C6C87 /* gfx_towns.cpp */; };
+ 8CD80CE0126272A0001C6C87 /* actor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CBF1262729F001C6C87 /* actor.cpp */; };
+ 8CD80CE1126272A0001C6C87 /* animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC11262729F001C6C87 /* animation.cpp */; };
+ 8CD80CE2126272A0001C6C87 /* callbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC31262729F001C6C87 /* callbacks.cpp */; };
+ 8CD80CE3126272A0001C6C87 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC41262729F001C6C87 /* console.cpp */; };
+ 8CD80CE4126272A0001C6C87 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC61262729F001C6C87 /* detection.cpp */; };
+ 8CD80CE5126272A0001C6C87 /* dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC71262729F001C6C87 /* dialog.cpp */; };
+ 8CD80CE6126272A0001C6C87 /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC91262729F001C6C87 /* font.cpp */; };
+ 8CD80CE7126272A0001C6C87 /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCB1262729F001C6C87 /* inventory.cpp */; };
+ 8CD80CE9126272A0001C6C87 /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCE1262729F001C6C87 /* music.cpp */; };
+ 8CD80CEA126272A0001C6C87 /* objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD01262729F001C6C87 /* objects.cpp */; };
+ 8CD80CEB126272A0001C6C87 /* pack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD21262729F001C6C87 /* pack.cpp */; };
+ 8CD80CEC126272A0001C6C87 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD4126272A0001C6C87 /* resources.cpp */; };
+ 8CD80CED126272A0001C6C87 /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD6126272A0001C6C87 /* scene.cpp */; };
+ 8CD80CEE126272A0001C6C87 /* segment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD8126272A0001C6C87 /* segment.cpp */; };
+ 8CD80CEF126272A0001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDA126272A0001C6C87 /* surface.cpp */; };
+ 8CD80CF0126272A0001C6C87 /* surface_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDC126272A0001C6C87 /* surface_list.cpp */; };
+ 8CD80CF1126272A0001C6C87 /* teenagent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDE126272A0001C6C87 /* teenagent.cpp */; };
+ 8CD80CF2126272A0001C6C87 /* actor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CBF1262729F001C6C87 /* actor.cpp */; };
+ 8CD80CF3126272A0001C6C87 /* animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC11262729F001C6C87 /* animation.cpp */; };
+ 8CD80CF4126272A0001C6C87 /* callbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC31262729F001C6C87 /* callbacks.cpp */; };
+ 8CD80CF5126272A0001C6C87 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC41262729F001C6C87 /* console.cpp */; };
+ 8CD80CF6126272A0001C6C87 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC61262729F001C6C87 /* detection.cpp */; };
+ 8CD80CF7126272A0001C6C87 /* dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC71262729F001C6C87 /* dialog.cpp */; };
+ 8CD80CF8126272A0001C6C87 /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC91262729F001C6C87 /* font.cpp */; };
+ 8CD80CF9126272A0001C6C87 /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCB1262729F001C6C87 /* inventory.cpp */; };
+ 8CD80CFB126272A0001C6C87 /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCE1262729F001C6C87 /* music.cpp */; };
+ 8CD80CFC126272A0001C6C87 /* objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD01262729F001C6C87 /* objects.cpp */; };
+ 8CD80CFD126272A0001C6C87 /* pack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD21262729F001C6C87 /* pack.cpp */; };
+ 8CD80CFE126272A0001C6C87 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD4126272A0001C6C87 /* resources.cpp */; };
+ 8CD80CFF126272A0001C6C87 /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD6126272A0001C6C87 /* scene.cpp */; };
+ 8CD80D00126272A0001C6C87 /* segment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD8126272A0001C6C87 /* segment.cpp */; };
+ 8CD80D01126272A0001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDA126272A0001C6C87 /* surface.cpp */; };
+ 8CD80D02126272A0001C6C87 /* surface_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDC126272A0001C6C87 /* surface_list.cpp */; };
+ 8CD80D03126272A0001C6C87 /* teenagent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDE126272A0001C6C87 /* teenagent.cpp */; };
+ 8CD80D04126272A0001C6C87 /* actor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CBF1262729F001C6C87 /* actor.cpp */; };
+ 8CD80D05126272A0001C6C87 /* animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC11262729F001C6C87 /* animation.cpp */; };
+ 8CD80D06126272A0001C6C87 /* callbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC31262729F001C6C87 /* callbacks.cpp */; };
+ 8CD80D07126272A0001C6C87 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC41262729F001C6C87 /* console.cpp */; };
+ 8CD80D08126272A0001C6C87 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC61262729F001C6C87 /* detection.cpp */; };
+ 8CD80D09126272A0001C6C87 /* dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC71262729F001C6C87 /* dialog.cpp */; };
+ 8CD80D0A126272A0001C6C87 /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CC91262729F001C6C87 /* font.cpp */; };
+ 8CD80D0B126272A0001C6C87 /* inventory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCB1262729F001C6C87 /* inventory.cpp */; };
+ 8CD80D0D126272A0001C6C87 /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CCE1262729F001C6C87 /* music.cpp */; };
+ 8CD80D0E126272A0001C6C87 /* objects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD01262729F001C6C87 /* objects.cpp */; };
+ 8CD80D0F126272A0001C6C87 /* pack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD21262729F001C6C87 /* pack.cpp */; };
+ 8CD80D10126272A0001C6C87 /* resources.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD4126272A0001C6C87 /* resources.cpp */; };
+ 8CD80D11126272A0001C6C87 /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD6126272A0001C6C87 /* scene.cpp */; };
+ 8CD80D12126272A0001C6C87 /* segment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CD8126272A0001C6C87 /* segment.cpp */; };
+ 8CD80D13126272A0001C6C87 /* surface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDA126272A0001C6C87 /* surface.cpp */; };
+ 8CD80D14126272A0001C6C87 /* surface_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDC126272A0001C6C87 /* surface_list.cpp */; };
+ 8CD80D15126272A0001C6C87 /* teenagent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD80CDE126272A0001C6C87 /* teenagent.cpp */; };
DF093E5F0F63CAD4002D821E /* pn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF093E5C0F63CAD4002D821E /* pn.cpp */; };
DF093E600F63CAD4002D821E /* script_pn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF093E5D0F63CAD4002D821E /* script_pn.cpp */; };
DF093E610F63CAD4002D821E /* vga_pn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF093E5E0F63CAD4002D821E /* vga_pn.cpp */; };
@@ -246,7 +429,6 @@
DF093F720F63CB26002D821E /* draw_bargon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421200E7BA6A700F5680E /* draw_bargon.cpp */; };
DF093F730F63CB26002D821E /* draw_v1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421210E7BA6A700F5680E /* draw_v1.cpp */; };
DF093F740F63CB26002D821E /* draw_v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421220E7BA6A700F5680E /* draw_v2.cpp */; };
- DF093F750F63CB26002D821E /* driver_vga.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421230E7BA6A700F5680E /* driver_vga.cpp */; };
DF093F760F63CB26002D821E /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421250E7BA6A700F5680E /* game.cpp */; };
DF093F790F63CB26002D821E /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421290E7BA6A700F5680E /* global.cpp */; };
DF093F7A0F63CB26002D821E /* gob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF84212B0E7BA6A700F5680E /* gob.cpp */; };
@@ -558,7 +740,6 @@
DF0940D80F63CB26002D821E /* insane_enemy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D50E7BA6AA00F5680E /* insane_enemy.cpp */; };
DF0940D90F63CB26002D821E /* insane_iact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D60E7BA6AA00F5680E /* insane_iact.cpp */; };
DF0940DA0F63CB26002D821E /* insane_scenes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D70E7BA6AA00F5680E /* insane_scenes.cpp */; };
- DF0940DB0F63CB26002D821E /* midiparser_eup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D90E7BA6AA00F5680E /* midiparser_eup.cpp */; };
DF0940DC0F63CB26002D821E /* midiparser_ro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DA0E7BA6AA00F5680E /* midiparser_ro.cpp */; };
DF0940DD0F63CB26002D821E /* nut_renderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DD0E7BA6AA00F5680E /* nut_renderer.cpp */; };
DF0940DE0F63CB26002D821E /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DF0E7BA6AA00F5680E /* object.cpp */; };
@@ -845,6 +1026,12 @@
DF09CC290FAC4EAB00A5AFD7 /* script_v4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF09CC270FAC4EAB00A5AFD7 /* script_v4.cpp */; };
DF09CC2A0FAC4EAB00A5AFD7 /* script_v3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF09CC260FAC4EAB00A5AFD7 /* script_v3.cpp */; };
DF09CC2B0FAC4EAB00A5AFD7 /* script_v4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF09CC270FAC4EAB00A5AFD7 /* script_v4.cpp */; };
+ DF0E303A1252C5BD0082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E30391252C5BD0082D593 /* cms.cpp */; };
+ DF0E303B1252C5BD0082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E30391252C5BD0082D593 /* cms.cpp */; };
+ DF0E303C1252C5BD0082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E30391252C5BD0082D593 /* cms.cpp */; };
+ DF0E30411252C6090082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E303F1252C6090082D593 /* cms.cpp */; };
+ DF0E30421252C6090082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E303F1252C6090082D593 /* cms.cpp */; };
+ DF0E30431252C6090082D593 /* cms.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0E303F1252C6090082D593 /* cms.cpp */; };
DF224E040FB23BC500C8E453 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF224E020FB23BC500C8E453 /* OpenGLES.framework */; };
DF224E050FB23BC500C8E453 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF224E020FB23BC500C8E453 /* OpenGLES.framework */; };
DF2EC3E510E6490800765801 /* browser_osx.mm in Sources */ = {isa = PBXBuildFile; fileRef = DF2EC3E410E6490800765801 /* browser_osx.mm */; };
@@ -962,12 +1149,10 @@
DF45B1D4116628A5009B85CC /* menu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18B116628A5009B85CC /* menu.cpp */; };
DF45B1D5116628A5009B85CC /* paint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18D116628A5009B85CC /* paint.cpp */; };
DF45B1D6116628A5009B85CC /* paint16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18F116628A5009B85CC /* paint16.cpp */; };
- DF45B1D7116628A5009B85CC /* paint32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B191116628A5009B85CC /* paint32.cpp */; };
DF45B1D8116628A5009B85CC /* palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B193116628A5009B85CC /* palette.cpp */; };
DF45B1D9116628A5009B85CC /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B195116628A5009B85CC /* picture.cpp */; };
DF45B1DA116628A5009B85CC /* portrait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B197116628A5009B85CC /* portrait.cpp */; };
DF45B1DB116628A5009B85CC /* ports.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B199116628A5009B85CC /* ports.cpp */; };
- DF45B1DC116628A5009B85CC /* robot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19B116628A5009B85CC /* robot.cpp */; };
DF45B1DD116628A5009B85CC /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19D116628A5009B85CC /* screen.cpp */; };
DF45B1DE116628A5009B85CC /* text16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19F116628A5009B85CC /* text16.cpp */; };
DF45B1DF116628A5009B85CC /* transitions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1A1116628A5009B85CC /* transitions.cpp */; };
@@ -984,7 +1169,6 @@
DF45B1F0116628A5009B85CC /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C1116628A5009B85CC /* music.cpp */; };
DF45B1F1116628A5009B85CC /* soundcmd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C3116628A5009B85CC /* soundcmd.cpp */; };
DF45B1F2116628A5009B85CC /* seq_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C6116628A5009B85CC /* seq_decoder.cpp */; };
- DF45B1F3116628A5009B85CC /* vmd_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C8116628A5009B85CC /* vmd_decoder.cpp */; };
DF45B1F4116628A5009B85CC /* animate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B176116628A5009B85CC /* animate.cpp */; };
DF45B1F5116628A5009B85CC /* cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B178116628A5009B85CC /* cache.cpp */; };
DF45B1F6116628A5009B85CC /* compare.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B17A116628A5009B85CC /* compare.cpp */; };
@@ -995,12 +1179,10 @@
DF45B1FE116628A5009B85CC /* menu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18B116628A5009B85CC /* menu.cpp */; };
DF45B1FF116628A5009B85CC /* paint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18D116628A5009B85CC /* paint.cpp */; };
DF45B200116628A5009B85CC /* paint16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18F116628A5009B85CC /* paint16.cpp */; };
- DF45B201116628A5009B85CC /* paint32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B191116628A5009B85CC /* paint32.cpp */; };
DF45B202116628A5009B85CC /* palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B193116628A5009B85CC /* palette.cpp */; };
DF45B203116628A5009B85CC /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B195116628A5009B85CC /* picture.cpp */; };
DF45B204116628A5009B85CC /* portrait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B197116628A5009B85CC /* portrait.cpp */; };
DF45B205116628A5009B85CC /* ports.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B199116628A5009B85CC /* ports.cpp */; };
- DF45B206116628A5009B85CC /* robot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19B116628A5009B85CC /* robot.cpp */; };
DF45B207116628A5009B85CC /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19D116628A5009B85CC /* screen.cpp */; };
DF45B208116628A5009B85CC /* text16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19F116628A5009B85CC /* text16.cpp */; };
DF45B209116628A5009B85CC /* transitions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1A1116628A5009B85CC /* transitions.cpp */; };
@@ -1017,7 +1199,6 @@
DF45B21A116628A5009B85CC /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C1116628A5009B85CC /* music.cpp */; };
DF45B21B116628A5009B85CC /* soundcmd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C3116628A5009B85CC /* soundcmd.cpp */; };
DF45B21C116628A5009B85CC /* seq_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C6116628A5009B85CC /* seq_decoder.cpp */; };
- DF45B21D116628A5009B85CC /* vmd_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C8116628A5009B85CC /* vmd_decoder.cpp */; };
DF45B21E116628A5009B85CC /* animate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B176116628A5009B85CC /* animate.cpp */; };
DF45B21F116628A5009B85CC /* cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B178116628A5009B85CC /* cache.cpp */; };
DF45B220116628A5009B85CC /* compare.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B17A116628A5009B85CC /* compare.cpp */; };
@@ -1028,12 +1209,10 @@
DF45B228116628A5009B85CC /* menu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18B116628A5009B85CC /* menu.cpp */; };
DF45B229116628A5009B85CC /* paint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18D116628A5009B85CC /* paint.cpp */; };
DF45B22A116628A5009B85CC /* paint16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B18F116628A5009B85CC /* paint16.cpp */; };
- DF45B22B116628A5009B85CC /* paint32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B191116628A5009B85CC /* paint32.cpp */; };
DF45B22C116628A5009B85CC /* palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B193116628A5009B85CC /* palette.cpp */; };
DF45B22D116628A5009B85CC /* picture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B195116628A5009B85CC /* picture.cpp */; };
DF45B22E116628A5009B85CC /* portrait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B197116628A5009B85CC /* portrait.cpp */; };
DF45B22F116628A5009B85CC /* ports.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B199116628A5009B85CC /* ports.cpp */; };
- DF45B230116628A5009B85CC /* robot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19B116628A5009B85CC /* robot.cpp */; };
DF45B231116628A5009B85CC /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19D116628A5009B85CC /* screen.cpp */; };
DF45B232116628A5009B85CC /* text16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B19F116628A5009B85CC /* text16.cpp */; };
DF45B233116628A5009B85CC /* transitions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1A1116628A5009B85CC /* transitions.cpp */; };
@@ -1050,7 +1229,6 @@
DF45B244116628A5009B85CC /* music.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C1116628A5009B85CC /* music.cpp */; };
DF45B245116628A5009B85CC /* soundcmd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C3116628A5009B85CC /* soundcmd.cpp */; };
DF45B246116628A5009B85CC /* seq_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C6116628A5009B85CC /* seq_decoder.cpp */; };
- DF45B247116628A5009B85CC /* vmd_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF45B1C8116628A5009B85CC /* vmd_decoder.cpp */; };
DF573C080F5A81EA00961A72 /* state.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF573C010F5A81EA00961A72 /* state.cpp */; };
DF573CBB0F5A85B300961A72 /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF573CBA0F5A85B300961A72 /* exec.cpp */; };
DF573CBE0F5A85E100961A72 /* timer_lol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF573CBD0F5A85E100961A72 /* timer_lol.cpp */; };
@@ -1075,18 +1253,12 @@
DF6118550FE3A8990042AD3F /* disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118540FE3A8990042AD3F /* disk.cpp */; };
DF6118560FE3A8990042AD3F /* disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118540FE3A8990042AD3F /* disk.cpp */; };
DF6118570FE3A8990042AD3F /* disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118540FE3A8990042AD3F /* disk.cpp */; };
- DF6118660FE3A9410042AD3F /* coktelvideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185C0FE3A9410042AD3F /* coktelvideo.cpp */; };
- DF6118670FE3A9410042AD3F /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185E0FE3A9410042AD3F /* indeo3.cpp */; };
DF6118680FE3A9410042AD3F /* dxa_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118600FE3A9410042AD3F /* dxa_decoder.cpp */; };
DF6118690FE3A9410042AD3F /* flic_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118620FE3A9410042AD3F /* flic_decoder.cpp */; };
DF61186A0FE3A9410042AD3F /* smk_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118640FE3A9410042AD3F /* smk_decoder.cpp */; };
- DF61186B0FE3A9410042AD3F /* coktelvideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185C0FE3A9410042AD3F /* coktelvideo.cpp */; };
- DF61186C0FE3A9410042AD3F /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185E0FE3A9410042AD3F /* indeo3.cpp */; };
DF61186D0FE3A9410042AD3F /* dxa_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118600FE3A9410042AD3F /* dxa_decoder.cpp */; };
DF61186E0FE3A9410042AD3F /* flic_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118620FE3A9410042AD3F /* flic_decoder.cpp */; };
DF61186F0FE3A9410042AD3F /* smk_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118640FE3A9410042AD3F /* smk_decoder.cpp */; };
- DF6118700FE3A9410042AD3F /* coktelvideo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185C0FE3A9410042AD3F /* coktelvideo.cpp */; };
- DF6118710FE3A9410042AD3F /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF61185E0FE3A9410042AD3F /* indeo3.cpp */; };
DF6118720FE3A9410042AD3F /* dxa_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118600FE3A9410042AD3F /* dxa_decoder.cpp */; };
DF6118730FE3A9410042AD3F /* flic_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118620FE3A9410042AD3F /* flic_decoder.cpp */; };
DF6118740FE3A9410042AD3F /* smk_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF6118640FE3A9410042AD3F /* smk_decoder.cpp */; };
@@ -1233,9 +1405,6 @@
DF7E8C110ED5FCC2001CB19F /* xmlparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7E8C0F0ED5FCC2001CB19F /* xmlparser.cpp */; };
DF7E8C530ED60067001CB19F /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7E8C510ED60067001CB19F /* game.cpp */; };
DF7E8C810ED60271001CB19F /* scummmodern.zip in Resources */ = {isa = PBXBuildFile; fileRef = DF7E8C7A0ED601E5001CB19F /* scummmodern.zip */; };
- DF7F285D11FF23B700159131 /* frameout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F285C11FF23B700159131 /* frameout.cpp */; };
- DF7F285E11FF23B700159131 /* frameout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F285C11FF23B700159131 /* frameout.cpp */; };
- DF7F285F11FF23B700159131 /* frameout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F285C11FF23B700159131 /* frameout.cpp */; };
DF7F286111FF23D500159131 /* amigamac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F286011FF23D500159131 /* amigamac.cpp */; };
DF7F286211FF23D500159131 /* amigamac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F286011FF23D500159131 /* amigamac.cpp */; };
DF7F286311FF23D500159131 /* amigamac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF7F286011FF23D500159131 /* amigamac.cpp */; };
@@ -1420,7 +1589,6 @@
DF8425E00E7BA6AC00F5680E /* draw_bargon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421200E7BA6A700F5680E /* draw_bargon.cpp */; };
DF8425E10E7BA6AC00F5680E /* draw_v1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421210E7BA6A700F5680E /* draw_v1.cpp */; };
DF8425E20E7BA6AC00F5680E /* draw_v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421220E7BA6A700F5680E /* draw_v2.cpp */; };
- DF8425E30E7BA6AC00F5680E /* driver_vga.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421230E7BA6A700F5680E /* driver_vga.cpp */; };
DF8425E40E7BA6AC00F5680E /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421250E7BA6A700F5680E /* game.cpp */; };
DF8425E70E7BA6AC00F5680E /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421290E7BA6A700F5680E /* global.cpp */; };
DF8425E80E7BA6AC00F5680E /* gob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF84212B0E7BA6A700F5680E /* gob.cpp */; };
@@ -1732,7 +1900,6 @@
DF8427AD0E7BA6AC00F5680E /* insane_enemy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D50E7BA6AA00F5680E /* insane_enemy.cpp */; };
DF8427AE0E7BA6AC00F5680E /* insane_iact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D60E7BA6AA00F5680E /* insane_iact.cpp */; };
DF8427AF0E7BA6AC00F5680E /* insane_scenes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D70E7BA6AA00F5680E /* insane_scenes.cpp */; };
- DF8427B00E7BA6AC00F5680E /* midiparser_eup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D90E7BA6AA00F5680E /* midiparser_eup.cpp */; };
DF8427B10E7BA6AC00F5680E /* midiparser_ro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DA0E7BA6AA00F5680E /* midiparser_ro.cpp */; };
DF8427B30E7BA6AC00F5680E /* nut_renderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DD0E7BA6AA00F5680E /* nut_renderer.cpp */; };
DF8427B40E7BA6AC00F5680E /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DF0E7BA6AA00F5680E /* object.cpp */; };
@@ -1901,6 +2068,57 @@
DF842A490E7BBBB400F5680E /* unarj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF842A450E7BBBB400F5680E /* unarj.cpp */; };
DF842A6D0E7BBD5700F5680E /* stdiostream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF842A6B0E7BBD5700F5680E /* stdiostream.cpp */; };
DF842A710E7BBDB200F5680E /* musicplugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF842A6F0E7BBDB200F5680E /* musicplugin.cpp */; };
+ DF895BFE124C24350077F6E8 /* coktel_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895BFC124C24350077F6E8 /* coktel_decoder.cpp */; };
+ DF895BFF124C24350077F6E8 /* coktel_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895BFC124C24350077F6E8 /* coktel_decoder.cpp */; };
+ DF895C00124C24350077F6E8 /* coktel_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895BFC124C24350077F6E8 /* coktel_decoder.cpp */; };
+ DF895C03124C24680077F6E8 /* player_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C01124C24680077F6E8 /* player_towns.cpp */; };
+ DF895C04124C24680077F6E8 /* player_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C01124C24680077F6E8 /* player_towns.cpp */; };
+ DF895C05124C24680077F6E8 /* player_towns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C01124C24680077F6E8 /* player_towns.cpp */; };
+ DF895C09124C24B60077F6E8 /* appleiigs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C08124C24B50077F6E8 /* appleiigs.cpp */; };
+ DF895C0A124C24B60077F6E8 /* appleiigs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C08124C24B50077F6E8 /* appleiigs.cpp */; };
+ DF895C0B124C24B60077F6E8 /* appleiigs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C08124C24B50077F6E8 /* appleiigs.cpp */; };
+ DF895C15124C24C10077F6E8 /* towns_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0D124C24C00077F6E8 /* towns_audio.cpp */; };
+ DF895C16124C24C10077F6E8 /* towns_euphony.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0F124C24C00077F6E8 /* towns_euphony.cpp */; };
+ DF895C17124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C11124C24C00077F6E8 /* towns_pc98_driver.cpp */; };
+ DF895C18124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C13124C24C00077F6E8 /* towns_pc98_fmsynth.cpp */; };
+ DF895C19124C24C10077F6E8 /* towns_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0D124C24C00077F6E8 /* towns_audio.cpp */; };
+ DF895C1A124C24C10077F6E8 /* towns_euphony.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0F124C24C00077F6E8 /* towns_euphony.cpp */; };
+ DF895C1B124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C11124C24C00077F6E8 /* towns_pc98_driver.cpp */; };
+ DF895C1C124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C13124C24C00077F6E8 /* towns_pc98_fmsynth.cpp */; };
+ DF895C1D124C24C10077F6E8 /* towns_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0D124C24C00077F6E8 /* towns_audio.cpp */; };
+ DF895C1E124C24C10077F6E8 /* towns_euphony.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C0F124C24C00077F6E8 /* towns_euphony.cpp */; };
+ DF895C1F124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C11124C24C00077F6E8 /* towns_pc98_driver.cpp */; };
+ DF895C20124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C13124C24C00077F6E8 /* towns_pc98_fmsynth.cpp */; };
+ DF895C25124C25150077F6E8 /* init_fascin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C24124C25150077F6E8 /* init_fascin.cpp */; };
+ DF895C26124C25150077F6E8 /* init_fascin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C24124C25150077F6E8 /* init_fascin.cpp */; };
+ DF895C27124C25150077F6E8 /* init_fascin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C24124C25150077F6E8 /* init_fascin.cpp */; };
+ DF895C2A124C25350077F6E8 /* script_patches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C29124C25350077F6E8 /* script_patches.cpp */; };
+ DF895C2B124C25350077F6E8 /* script_patches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C29124C25350077F6E8 /* script_patches.cpp */; };
+ DF895C2C124C25350077F6E8 /* script_patches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895C29124C25350077F6E8 /* script_patches.cpp */; };
+ DF895C34124C26660077F6E8 /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C33124C26660077F6E8 /* icon-72.png */; };
+ DF895C35124C26660077F6E8 /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C33124C26660077F6E8 /* icon-72.png */; };
+ DF895C36124C26660077F6E8 /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C33124C26660077F6E8 /* icon-72.png */; };
+ DF895C41124C271F0077F6E8 /* icon4.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C40124C271F0077F6E8 /* icon4.png */; };
+ DF895C42124C271F0077F6E8 /* icon4.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C40124C271F0077F6E8 /* icon4.png */; };
+ DF895C43124C271F0077F6E8 /* icon4.png in Resources */ = {isa = PBXBuildFile; fileRef = DF895C40124C271F0077F6E8 /* icon4.png */; };
+ DF895CB8124E58980077F6E8 /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAB124E58980077F6E8 /* indeo3.cpp */; };
+ DF895CB9124E58980077F6E8 /* mjpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAD124E58980077F6E8 /* mjpeg.cpp */; };
+ DF895CBA124E58980077F6E8 /* qdm2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAF124E58980077F6E8 /* qdm2.cpp */; };
+ DF895CBB124E58980077F6E8 /* qtrle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB2124E58980077F6E8 /* qtrle.cpp */; };
+ DF895CBC124E58980077F6E8 /* rpza.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB4124E58980077F6E8 /* rpza.cpp */; };
+ DF895CBD124E58990077F6E8 /* smc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB6124E58980077F6E8 /* smc.cpp */; };
+ DF895CBE124E58990077F6E8 /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAB124E58980077F6E8 /* indeo3.cpp */; };
+ DF895CBF124E58990077F6E8 /* mjpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAD124E58980077F6E8 /* mjpeg.cpp */; };
+ DF895CC0124E58990077F6E8 /* qdm2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAF124E58980077F6E8 /* qdm2.cpp */; };
+ DF895CC1124E58990077F6E8 /* qtrle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB2124E58980077F6E8 /* qtrle.cpp */; };
+ DF895CC2124E58990077F6E8 /* rpza.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB4124E58980077F6E8 /* rpza.cpp */; };
+ DF895CC3124E58990077F6E8 /* smc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB6124E58980077F6E8 /* smc.cpp */; };
+ DF895CC4124E58990077F6E8 /* indeo3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAB124E58980077F6E8 /* indeo3.cpp */; };
+ DF895CC5124E58990077F6E8 /* mjpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAD124E58980077F6E8 /* mjpeg.cpp */; };
+ DF895CC6124E58990077F6E8 /* qdm2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CAF124E58980077F6E8 /* qdm2.cpp */; };
+ DF895CC7124E58990077F6E8 /* qtrle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB2124E58980077F6E8 /* qtrle.cpp */; };
+ DF895CC8124E58990077F6E8 /* rpza.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB4124E58980077F6E8 /* rpza.cpp */; };
+ DF895CC9124E58990077F6E8 /* smc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF895CB6124E58980077F6E8 /* smc.cpp */; };
DF89C2880F62D55C00D756B6 /* sprites_lol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF89C2870F62D55C00D756B6 /* sprites_lol.cpp */; };
DF89C2A40F62D79E00D756B6 /* script.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF89C2A30F62D79E00D756B6 /* script.cpp */; };
DF89C2BB0F62D91000D756B6 /* timestamp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF89C2B80F62D91000D756B6 /* timestamp.cpp */; };
@@ -2350,7 +2568,6 @@
DFF95A080FB22D5700A3EC78 /* draw_bargon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421200E7BA6A700F5680E /* draw_bargon.cpp */; };
DFF95A090FB22D5700A3EC78 /* draw_v1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421210E7BA6A700F5680E /* draw_v1.cpp */; };
DFF95A0A0FB22D5700A3EC78 /* draw_v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421220E7BA6A700F5680E /* draw_v2.cpp */; };
- DFF95A0B0FB22D5700A3EC78 /* driver_vga.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421230E7BA6A700F5680E /* driver_vga.cpp */; };
DFF95A0C0FB22D5700A3EC78 /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421250E7BA6A700F5680E /* game.cpp */; };
DFF95A0F0FB22D5700A3EC78 /* global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8421290E7BA6A700F5680E /* global.cpp */; };
DFF95A100FB22D5700A3EC78 /* gob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF84212B0E7BA6A700F5680E /* gob.cpp */; };
@@ -2662,7 +2879,6 @@
DFF95B6D0FB22D5700A3EC78 /* insane_enemy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D50E7BA6AA00F5680E /* insane_enemy.cpp */; };
DFF95B6E0FB22D5700A3EC78 /* insane_iact.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D60E7BA6AA00F5680E /* insane_iact.cpp */; };
DFF95B6F0FB22D5700A3EC78 /* insane_scenes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D70E7BA6AA00F5680E /* insane_scenes.cpp */; };
- DFF95B700FB22D5700A3EC78 /* midiparser_eup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423D90E7BA6AA00F5680E /* midiparser_eup.cpp */; };
DFF95B710FB22D5700A3EC78 /* midiparser_ro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DA0E7BA6AA00F5680E /* midiparser_ro.cpp */; };
DFF95B720FB22D5700A3EC78 /* nut_renderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DD0E7BA6AA00F5680E /* nut_renderer.cpp */; };
DFF95B730FB22D5700A3EC78 /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF8423DF0E7BA6AA00F5680E /* object.cpp */; };
@@ -2970,6 +3186,120 @@
/* Begin PBXFileReference section */
1D6058910D05DD3D006BFB54 /* ScummVM.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScummVM.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8CB5A9B71253FD6800CB6BC7 /* m4_scene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = m4_scene.cpp; sourceTree = "<group>"; };
+ 8CB5A9B81253FD6800CB6BC7 /* m4_scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = m4_scene.h; sourceTree = "<group>"; };
+ 8CB5A9B91253FD6800CB6BC7 /* mads_logic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mads_logic.cpp; sourceTree = "<group>"; };
+ 8CB5A9BA1253FD6800CB6BC7 /* mads_logic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mads_logic.h; sourceTree = "<group>"; };
+ 8CB5A9BB1253FD6800CB6BC7 /* mads_player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mads_player.cpp; sourceTree = "<group>"; };
+ 8CB5A9BC1253FD6800CB6BC7 /* mads_player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mads_player.h; sourceTree = "<group>"; };
+ 8CB5A9BD1253FD6800CB6BC7 /* mads_scene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mads_scene.cpp; sourceTree = "<group>"; };
+ 8CB5A9BE1253FD6800CB6BC7 /* mads_scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mads_scene.h; sourceTree = "<group>"; };
+ 8CB5A9BF1253FD6900CB6BC7 /* mads_views.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mads_views.cpp; sourceTree = "<group>"; };
+ 8CB5A9C01253FD6900CB6BC7 /* mads_views.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mads_views.h; sourceTree = "<group>"; };
+ 8CB5A9D51253FDF400CB6BC7 /* drascula.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = drascula.dat; sourceTree = "<group>"; };
+ 8CB5A9D61253FDF500CB6BC7 /* hugo.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = hugo.dat; sourceTree = "<group>"; };
+ 8CB5A9D71253FDF500CB6BC7 /* m4.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = m4.dat; sourceTree = "<group>"; };
+ 8CB5A9D81253FDF500CB6BC7 /* teenagent.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = teenagent.dat; sourceTree = "<group>"; };
+ 8CD1ECC6126202AA00FA198C /* detection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = detection.cpp; sourceTree = "<group>"; };
+ 8CD1ECC7126202AA00FA198C /* display.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = display.cpp; sourceTree = "<group>"; };
+ 8CD1ECC8126202AA00FA198C /* display.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = display.h; sourceTree = "<group>"; };
+ 8CD1ECC9126202AA00FA198C /* engine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = engine.cpp; sourceTree = "<group>"; };
+ 8CD1ECCA126202AA00FA198C /* engine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = engine.h; sourceTree = "<group>"; };
+ 8CD1ECCB126202AA00FA198C /* file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file.cpp; sourceTree = "<group>"; };
+ 8CD1ECCC126202AA00FA198C /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
+ 8CD1ECCD126202AA00FA198C /* game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = game.h; sourceTree = "<group>"; };
+ 8CD1ECCE126202AA00FA198C /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global.h; sourceTree = "<group>"; };
+ 8CD1ECCF126202AA00FA198C /* hugo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hugo.cpp; sourceTree = "<group>"; };
+ 8CD1ECD0126202AA00FA198C /* hugo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hugo.h; sourceTree = "<group>"; };
+ 8CD1ECD1126202AA00FA198C /* intro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = intro.cpp; sourceTree = "<group>"; };
+ 8CD1ECD2126202AA00FA198C /* intro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intro.h; sourceTree = "<group>"; };
+ 8CD1ECD3126202AA00FA198C /* inventory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inventory.cpp; sourceTree = "<group>"; };
+ 8CD1ECD4126202AA00FA198C /* inventory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inventory.h; sourceTree = "<group>"; };
+ 8CD1ECD7126202AA00FA198C /* mouse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mouse.cpp; sourceTree = "<group>"; };
+ 8CD1ECD8126202AA00FA198C /* mouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mouse.h; sourceTree = "<group>"; };
+ 8CD1ECD9126202AA00FA198C /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cpp; sourceTree = "<group>"; };
+ 8CD1ECDA126202AA00FA198C /* parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = "<group>"; };
+ 8CD1ECDB126202AA00FA198C /* route.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = route.cpp; sourceTree = "<group>"; };
+ 8CD1ECDC126202AA00FA198C /* route.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = route.h; sourceTree = "<group>"; };
+ 8CD1ECDD126202AA00FA198C /* schedule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = schedule.cpp; sourceTree = "<group>"; };
+ 8CD1ECDE126202AA00FA198C /* schedule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = schedule.h; sourceTree = "<group>"; };
+ 8CD1ECDF126202AA00FA198C /* sound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sound.cpp; sourceTree = "<group>"; };
+ 8CD1ECE0126202AA00FA198C /* sound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sound.h; sourceTree = "<group>"; };
+ 8CD1ECE1126202AA00FA198C /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util.cpp; sourceTree = "<group>"; };
+ 8CD1ECE2126202AA00FA198C /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; };
+ 8CD1ECE4126202AA00FA198C /* anim.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = anim.cpp; sourceTree = "<group>"; };
+ 8CD1ECE5126202AA00FA198C /* anim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = anim.h; sourceTree = "<group>"; };
+ 8CD1ECE6126202AA00FA198C /* audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio.cpp; sourceTree = "<group>"; };
+ 8CD1ECE7126202AA00FA198C /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio.h; sourceTree = "<group>"; };
+ 8CD1ECE8126202AA00FA198C /* character.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = character.cpp; sourceTree = "<group>"; };
+ 8CD1ECE9126202AA00FA198C /* character.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = character.h; sourceTree = "<group>"; };
+ 8CD1ECEA126202AA00FA198C /* conversation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = conversation.cpp; sourceTree = "<group>"; };
+ 8CD1ECEB126202AA00FA198C /* conversation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = conversation.h; sourceTree = "<group>"; };
+ 8CD1ECEC126202AA00FA198C /* detection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = detection.cpp; sourceTree = "<group>"; };
+ 8CD1ECED126202AA00FA198C /* drew.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drew.cpp; sourceTree = "<group>"; };
+ 8CD1ECEE126202AA00FA198C /* drew.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = drew.h; sourceTree = "<group>"; };
+ 8CD1ECEF126202AA00FA198C /* flux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flux.cpp; sourceTree = "<group>"; };
+ 8CD1ECF0126202AA00FA198C /* flux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flux.h; sourceTree = "<group>"; };
+ 8CD1ECF1126202AA00FA198C /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = "<group>"; };
+ 8CD1ECF2126202AA00FA198C /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = "<group>"; };
+ 8CD1ECF3126202AA00FA198C /* hotspot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hotspot.cpp; sourceTree = "<group>"; };
+ 8CD1ECF4126202AA00FA198C /* hotspot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hotspot.h; sourceTree = "<group>"; };
+ 8CD1ECF7126202AA00FA198C /* movie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = movie.cpp; sourceTree = "<group>"; };
+ 8CD1ECF8126202AA00FA198C /* movie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = movie.h; sourceTree = "<group>"; };
+ 8CD1ECF9126202AA00FA198C /* path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path.cpp; sourceTree = "<group>"; };
+ 8CD1ECFA126202AA00FA198C /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = path.h; sourceTree = "<group>"; };
+ 8CD1ECFB126202AA00FA198C /* picture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = picture.cpp; sourceTree = "<group>"; };
+ 8CD1ECFC126202AA00FA198C /* picture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = picture.h; sourceTree = "<group>"; };
+ 8CD1ECFD126202AA00FA198C /* resource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource.cpp; sourceTree = "<group>"; };
+ 8CD1ECFE126202AA00FA198C /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = "<group>"; };
+ 8CD1ECFF126202AA00FA198C /* script.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script.cpp; sourceTree = "<group>"; };
+ 8CD1ED00126202AA00FA198C /* script.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = script.h; sourceTree = "<group>"; };
+ 8CD1ED01126202AA00FA198C /* script_func.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script_func.cpp; sourceTree = "<group>"; };
+ 8CD1ED02126202AA00FA198C /* script_func.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = script_func.h; sourceTree = "<group>"; };
+ 8CD1ED03126202AA00FA198C /* state.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = state.cpp; sourceTree = "<group>"; };
+ 8CD1ED04126202AA00FA198C /* state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = state.h; sourceTree = "<group>"; };
+ 8CD1ED05126202AA00FA198C /* text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text.cpp; sourceTree = "<group>"; };
+ 8CD1ED06126202AA00FA198C /* text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text.h; sourceTree = "<group>"; };
+ 8CD1ED07126202AA00FA198C /* tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tools.cpp; sourceTree = "<group>"; };
+ 8CD1ED08126202AA00FA198C /* tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tools.h; sourceTree = "<group>"; };
+ 8CD1ED09126202AA00FA198C /* toon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = toon.cpp; sourceTree = "<group>"; };
+ 8CD1ED0A126202AA00FA198C /* toon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = toon.h; sourceTree = "<group>"; };
+ 8CD1ED871262030100FA198C /* toon.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = toon.dat; sourceTree = "<group>"; };
+ 8CD80C89126271A9001C6C87 /* surface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = surface.cpp; sourceTree = "<group>"; };
+ 8CD80C8A126271A9001C6C87 /* surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = surface.h; sourceTree = "<group>"; };
+ 8CD80C90126271BD001C6C87 /* gfx_towns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gfx_towns.cpp; sourceTree = "<group>"; };
+ 8CD80CBF1262729F001C6C87 /* actor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = actor.cpp; sourceTree = "<group>"; };
+ 8CD80CC01262729F001C6C87 /* actor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = actor.h; sourceTree = "<group>"; };
+ 8CD80CC11262729F001C6C87 /* animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = animation.cpp; sourceTree = "<group>"; };
+ 8CD80CC21262729F001C6C87 /* animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = animation.h; sourceTree = "<group>"; };
+ 8CD80CC31262729F001C6C87 /* callbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = callbacks.cpp; sourceTree = "<group>"; };
+ 8CD80CC41262729F001C6C87 /* console.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = console.cpp; sourceTree = "<group>"; };
+ 8CD80CC51262729F001C6C87 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = "<group>"; };
+ 8CD80CC61262729F001C6C87 /* detection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = detection.cpp; sourceTree = "<group>"; };
+ 8CD80CC71262729F001C6C87 /* dialog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dialog.cpp; sourceTree = "<group>"; };
+ 8CD80CC81262729F001C6C87 /* dialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dialog.h; sourceTree = "<group>"; };
+ 8CD80CC91262729F001C6C87 /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = "<group>"; };
+ 8CD80CCA1262729F001C6C87 /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font.h; sourceTree = "<group>"; };
+ 8CD80CCB1262729F001C6C87 /* inventory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = inventory.cpp; sourceTree = "<group>"; };
+ 8CD80CCC1262729F001C6C87 /* inventory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = inventory.h; sourceTree = "<group>"; };
+ 8CD80CCE1262729F001C6C87 /* music.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = music.cpp; sourceTree = "<group>"; };
+ 8CD80CCF1262729F001C6C87 /* music.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = music.h; sourceTree = "<group>"; };
+ 8CD80CD01262729F001C6C87 /* objects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = objects.cpp; sourceTree = "<group>"; };
+ 8CD80CD11262729F001C6C87 /* objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = objects.h; sourceTree = "<group>"; };
+ 8CD80CD21262729F001C6C87 /* pack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pack.cpp; sourceTree = "<group>"; };
+ 8CD80CD3126272A0001C6C87 /* pack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pack.h; sourceTree = "<group>"; };
+ 8CD80CD4126272A0001C6C87 /* resources.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resources.cpp; sourceTree = "<group>"; };
+ 8CD80CD5126272A0001C6C87 /* resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resources.h; sourceTree = "<group>"; };
+ 8CD80CD6126272A0001C6C87 /* scene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scene.cpp; sourceTree = "<group>"; };
+ 8CD80CD7126272A0001C6C87 /* scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scene.h; sourceTree = "<group>"; };
+ 8CD80CD8126272A0001C6C87 /* segment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = segment.cpp; sourceTree = "<group>"; };
+ 8CD80CD9126272A0001C6C87 /* segment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = segment.h; sourceTree = "<group>"; };
+ 8CD80CDA126272A0001C6C87 /* surface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = surface.cpp; sourceTree = "<group>"; };
+ 8CD80CDB126272A0001C6C87 /* surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = surface.h; sourceTree = "<group>"; };
+ 8CD80CDC126272A0001C6C87 /* surface_list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = surface_list.cpp; sourceTree = "<group>"; };
+ 8CD80CDD126272A0001C6C87 /* surface_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = surface_list.h; sourceTree = "<group>"; };
+ 8CD80CDE126272A0001C6C87 /* teenagent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = teenagent.cpp; sourceTree = "<group>"; };
+ 8CD80CDF126272A0001C6C87 /* teenagent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = teenagent.h; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DF093E5C0F63CAD4002D821E /* pn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pn.cpp; sourceTree = "<group>"; };
DF093E5D0F63CAD4002D821E /* script_pn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script_pn.cpp; sourceTree = "<group>"; };
@@ -3008,6 +3338,9 @@
DF09CC240FAC4E6200A5AFD7 /* scumm_v8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scumm_v8.h; sourceTree = "<group>"; };
DF09CC260FAC4EAB00A5AFD7 /* script_v3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script_v3.cpp; sourceTree = "<group>"; };
DF09CC270FAC4EAB00A5AFD7 /* script_v4.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script_v4.cpp; sourceTree = "<group>"; };
+ DF0E30391252C5BD0082D593 /* cms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cms.cpp; sourceTree = "<group>"; };
+ DF0E303F1252C6090082D593 /* cms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cms.cpp; sourceTree = "<group>"; };
+ DF0E30401252C6090082D593 /* cms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cms.h; sourceTree = "<group>"; };
DF224E020FB23BC500C8E453 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
DF2EC3E410E6490800765801 /* browser_osx.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = browser_osx.mm; sourceTree = "<group>"; };
DF2EC3F610E64C0C00765801 /* dialogs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dialogs.cpp; sourceTree = "<group>"; };
@@ -3190,8 +3523,6 @@
DF45B1C4116628A5009B85CC /* soundcmd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = soundcmd.h; sourceTree = "<group>"; };
DF45B1C6116628A5009B85CC /* seq_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = seq_decoder.cpp; sourceTree = "<group>"; };
DF45B1C7116628A5009B85CC /* seq_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = seq_decoder.h; sourceTree = "<group>"; };
- DF45B1C8116628A5009B85CC /* vmd_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vmd_decoder.cpp; sourceTree = "<group>"; };
- DF45B1C9116628A5009B85CC /* vmd_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vmd_decoder.h; sourceTree = "<group>"; };
DF573BFE0F5A81EA00961A72 /* kernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kernel.h; sourceTree = "<group>"; };
DF573BFF0F5A81EA00961A72 /* script.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = script.h; sourceTree = "<group>"; };
DF573C000F5A81EA00961A72 /* seg_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = seg_manager.h; sourceTree = "<group>"; };
@@ -3216,10 +3547,6 @@
DF6118460FE3A8250042AD3F /* resource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource.h; sourceTree = "<group>"; };
DF6118540FE3A8990042AD3F /* disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = disk.cpp; sourceTree = "<group>"; };
DF6118590FE3A9020042AD3F /* helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helper.h; sourceTree = "<group>"; };
- DF61185C0FE3A9410042AD3F /* coktelvideo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coktelvideo.cpp; sourceTree = "<group>"; };
- DF61185D0FE3A9410042AD3F /* coktelvideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coktelvideo.h; sourceTree = "<group>"; };
- DF61185E0FE3A9410042AD3F /* indeo3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = indeo3.cpp; sourceTree = "<group>"; };
- DF61185F0FE3A9410042AD3F /* indeo3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = indeo3.h; sourceTree = "<group>"; };
DF6118600FE3A9410042AD3F /* dxa_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dxa_decoder.cpp; sourceTree = "<group>"; };
DF6118610FE3A9410042AD3F /* dxa_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dxa_decoder.h; sourceTree = "<group>"; };
DF6118620FE3A9410042AD3F /* flic_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flic_decoder.cpp; sourceTree = "<group>"; };
@@ -3562,8 +3889,6 @@
DF8421200E7BA6A700F5680E /* draw_bargon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = draw_bargon.cpp; sourceTree = "<group>"; };
DF8421210E7BA6A700F5680E /* draw_v1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = draw_v1.cpp; sourceTree = "<group>"; };
DF8421220E7BA6A700F5680E /* draw_v2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = draw_v2.cpp; sourceTree = "<group>"; };
- DF8421230E7BA6A700F5680E /* driver_vga.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = driver_vga.cpp; sourceTree = "<group>"; };
- DF8421240E7BA6A700F5680E /* driver_vga.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = driver_vga.h; sourceTree = "<group>"; };
DF8421250E7BA6A700F5680E /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = game.cpp; sourceTree = "<group>"; };
DF8421260E7BA6A700F5680E /* game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = game.h; sourceTree = "<group>"; };
DF8421290E7BA6A700F5680E /* global.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = global.cpp; sourceTree = "<group>"; };
@@ -4083,7 +4408,6 @@
DF8423D50E7BA6AA00F5680E /* insane_enemy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = insane_enemy.cpp; sourceTree = "<group>"; };
DF8423D60E7BA6AA00F5680E /* insane_iact.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = insane_iact.cpp; sourceTree = "<group>"; };
DF8423D70E7BA6AA00F5680E /* insane_scenes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = insane_scenes.cpp; sourceTree = "<group>"; };
- DF8423D90E7BA6AA00F5680E /* midiparser_eup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = midiparser_eup.cpp; sourceTree = "<group>"; };
DF8423DA0E7BA6AA00F5680E /* midiparser_ro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = midiparser_ro.cpp; sourceTree = "<group>"; };
DF8423DC0E7BA6AA00F5680E /* music.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = music.h; sourceTree = "<group>"; };
DF8423DD0E7BA6AA00F5680E /* nut_renderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nut_renderer.cpp; sourceTree = "<group>"; };
@@ -4383,6 +4707,38 @@
DF842A6C0E7BBD5700F5680E /* stdiostream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdiostream.h; sourceTree = "<group>"; };
DF842A6F0E7BBDB200F5680E /* musicplugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = musicplugin.cpp; sourceTree = "<group>"; };
DF842A700E7BBDB200F5680E /* musicplugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = musicplugin.h; sourceTree = "<group>"; };
+ DF895BFC124C24350077F6E8 /* coktel_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coktel_decoder.cpp; sourceTree = "<group>"; };
+ DF895BFD124C24350077F6E8 /* coktel_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coktel_decoder.h; sourceTree = "<group>"; };
+ DF895C01124C24680077F6E8 /* player_towns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = player_towns.cpp; sourceTree = "<group>"; };
+ DF895C02124C24680077F6E8 /* player_towns.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = player_towns.h; sourceTree = "<group>"; };
+ DF895C08124C24B50077F6E8 /* appleiigs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = appleiigs.cpp; sourceTree = "<group>"; };
+ DF895C0D124C24C00077F6E8 /* towns_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = towns_audio.cpp; sourceTree = "<group>"; };
+ DF895C0E124C24C00077F6E8 /* towns_audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = towns_audio.h; sourceTree = "<group>"; };
+ DF895C0F124C24C00077F6E8 /* towns_euphony.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = towns_euphony.cpp; sourceTree = "<group>"; };
+ DF895C10124C24C00077F6E8 /* towns_euphony.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = towns_euphony.h; sourceTree = "<group>"; };
+ DF895C11124C24C00077F6E8 /* towns_pc98_driver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = towns_pc98_driver.cpp; sourceTree = "<group>"; };
+ DF895C12124C24C00077F6E8 /* towns_pc98_driver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = towns_pc98_driver.h; sourceTree = "<group>"; };
+ DF895C13124C24C00077F6E8 /* towns_pc98_fmsynth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = towns_pc98_fmsynth.cpp; sourceTree = "<group>"; };
+ DF895C14124C24C00077F6E8 /* towns_pc98_fmsynth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = towns_pc98_fmsynth.h; sourceTree = "<group>"; };
+ DF895C23124C25150077F6E8 /* detection_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = detection_tables.h; sourceTree = "<group>"; };
+ DF895C24124C25150077F6E8 /* init_fascin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = init_fascin.cpp; sourceTree = "<group>"; };
+ DF895C28124C25350077F6E8 /* kernel_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kernel_tables.h; sourceTree = "<group>"; };
+ DF895C29124C25350077F6E8 /* script_patches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script_patches.cpp; sourceTree = "<group>"; };
+ DF895C33124C26660077F6E8 /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = "<group>"; };
+ DF895C40124C271F0077F6E8 /* icon4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon4.png; sourceTree = "<group>"; };
+ DF895CAB124E58980077F6E8 /* indeo3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = indeo3.cpp; sourceTree = "<group>"; };
+ DF895CAC124E58980077F6E8 /* indeo3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = indeo3.h; sourceTree = "<group>"; };
+ DF895CAD124E58980077F6E8 /* mjpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mjpeg.cpp; sourceTree = "<group>"; };
+ DF895CAE124E58980077F6E8 /* mjpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mjpeg.h; sourceTree = "<group>"; };
+ DF895CAF124E58980077F6E8 /* qdm2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qdm2.cpp; sourceTree = "<group>"; };
+ DF895CB0124E58980077F6E8 /* qdm2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qdm2.h; sourceTree = "<group>"; };
+ DF895CB1124E58980077F6E8 /* qdm2data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qdm2data.h; sourceTree = "<group>"; };
+ DF895CB2124E58980077F6E8 /* qtrle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qtrle.cpp; sourceTree = "<group>"; };
+ DF895CB3124E58980077F6E8 /* qtrle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qtrle.h; sourceTree = "<group>"; };
+ DF895CB4124E58980077F6E8 /* rpza.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rpza.cpp; sourceTree = "<group>"; };
+ DF895CB5124E58980077F6E8 /* rpza.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rpza.h; sourceTree = "<group>"; };
+ DF895CB6124E58980077F6E8 /* smc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smc.cpp; sourceTree = "<group>"; };
+ DF895CB7124E58980077F6E8 /* smc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = smc.h; sourceTree = "<group>"; };
DF89C2870F62D55C00D756B6 /* sprites_lol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sprites_lol.cpp; sourceTree = "<group>"; };
DF89C2A30F62D79E00D756B6 /* script.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = script.cpp; sourceTree = "<group>"; };
DF89C2B80F62D91000D756B6 /* timestamp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timestamp.cpp; sourceTree = "<group>"; };
@@ -4652,10 +5008,10 @@
DFE478220D81F4E900B6D1FB /* pcspk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pcspk.h; sourceTree = "<group>"; };
DFE478230D81F4E900B6D1FB /* ym2612.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ym2612.cpp; sourceTree = "<group>"; };
DFE478240D81F4E900B6D1FB /* ym2612.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ym2612.h; sourceTree = "<group>"; };
- DFE47C810D81F86900B6D1FB /* kyra.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = kyra.dat; path = "../engine-data/kyra.dat"; sourceTree = "<group>"; };
- DFE47C820D81F86900B6D1FB /* lure.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = lure.dat; path = "../engine-data/lure.dat"; sourceTree = "<group>"; };
- DFE47C830D81F86900B6D1FB /* queen.tbl */ = {isa = PBXFileReference; lastKnownFileType = file; name = queen.tbl; path = "../engine-data/queen.tbl"; sourceTree = "<group>"; };
- DFE47C850D81F86900B6D1FB /* sky.cpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = sky.cpt; path = "../engine-data/sky.cpt"; sourceTree = "<group>"; };
+ DFE47C810D81F86900B6D1FB /* kyra.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = kyra.dat; sourceTree = "<group>"; };
+ DFE47C820D81F86900B6D1FB /* lure.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = lure.dat; sourceTree = "<group>"; };
+ DFE47C830D81F86900B6D1FB /* queen.tbl */ = {isa = PBXFileReference; lastKnownFileType = file; path = queen.tbl; sourceTree = "<group>"; };
+ DFE47C850D81F86900B6D1FB /* sky.cpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = sky.cpt; sourceTree = "<group>"; };
DFE88C440F874A1100C555C5 /* sound.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sound.cpp; sourceTree = "<group>"; };
DFE88C450F874A1100C555C5 /* sound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sound.h; sourceTree = "<group>"; };
DFEC5D0A1166C5CF00C90552 /* random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = random.cpp; sourceTree = "<group>"; };
@@ -4774,11 +5130,11 @@
DFD6476A0F49F7D2008E18EF /* libs */,
DF841FF50E7BA6A600F5680E /* engines */,
DFE470D50D81F4E700B6D1FB /* backends */,
+ DFE470C00D81F4BA00B6D1FB /* base */,
DFE473950D81F4E800B6D1FB /* common */,
DFE477520D81F4E900B6D1FB /* graphics */,
DFE477880D81F4E900B6D1FB /* gui */,
DFE477C60D81F4E900B6D1FB /* sound */,
- DFE470C00D81F4BA00B6D1FB /* base */,
29B97317FDCFA39411CA2CEA /* Resources */,
DFA2A57A118E433A00344DFD /* Resources-iPad */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
@@ -4790,14 +5146,13 @@
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
+ 8CB5A9D21253FD9100CB6BC7 /* engine-data */,
+ DF895C40124C271F0077F6E8 /* icon4.png */,
+ DF895C33124C26660077F6E8 /* icon-72.png */,
DF0944B00F6430ED002D821E /* scummvm.icns */,
DFF4DFFC0F4B449F00C50BC7 /* Info.plist.in */,
DF2FFD290F48717F0006E566 /* Default.png */,
DF2FFD2A0F48717F0006E566 /* icon.png */,
- DFE47C810D81F86900B6D1FB /* kyra.dat */,
- DFE47C820D81F86900B6D1FB /* lure.dat */,
- DFE47C830D81F86900B6D1FB /* queen.tbl */,
- DFE47C850D81F86900B6D1FB /* sky.cpt */,
8D1107310486CEB800E47090 /* Info.plist */,
);
name = Resources;
@@ -4812,6 +5167,140 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 8CB5A9D21253FD9100CB6BC7 /* engine-data */ = {
+ isa = PBXGroup;
+ children = (
+ 8CB5A9D51253FDF400CB6BC7 /* drascula.dat */,
+ 8CB5A9D61253FDF500CB6BC7 /* hugo.dat */,
+ DFE47C810D81F86900B6D1FB /* kyra.dat */,
+ DFE47C820D81F86900B6D1FB /* lure.dat */,
+ 8CB5A9D71253FDF500CB6BC7 /* m4.dat */,
+ DFE47C830D81F86900B6D1FB /* queen.tbl */,
+ DFE47C850D81F86900B6D1FB /* sky.cpt */,
+ 8CB5A9D81253FDF500CB6BC7 /* teenagent.dat */,
+ 8CD1ED871262030100FA198C /* toon.dat */,
+ );
+ name = "engine-data";
+ path = "../engine-data";
+ sourceTree = "<group>";
+ };
+ 8CD1ECC5126202AA00FA198C /* hugo */ = {
+ isa = PBXGroup;
+ children = (
+ 8CD1ECC6126202AA00FA198C /* detection.cpp */,
+ 8CD1ECC7126202AA00FA198C /* display.cpp */,
+ 8CD1ECC8126202AA00FA198C /* display.h */,
+ 8CD1ECC9126202AA00FA198C /* engine.cpp */,
+ 8CD1ECCA126202AA00FA198C /* engine.h */,
+ 8CD1ECCB126202AA00FA198C /* file.cpp */,
+ 8CD1ECCC126202AA00FA198C /* file.h */,
+ 8CD1ECCD126202AA00FA198C /* game.h */,
+ 8CD1ECCE126202AA00FA198C /* global.h */,
+ 8CD1ECCF126202AA00FA198C /* hugo.cpp */,
+ 8CD1ECD0126202AA00FA198C /* hugo.h */,
+ 8CD1ECD1126202AA00FA198C /* intro.cpp */,
+ 8CD1ECD2126202AA00FA198C /* intro.h */,
+ 8CD1ECD3126202AA00FA198C /* inventory.cpp */,
+ 8CD1ECD4126202AA00FA198C /* inventory.h */,
+ 8CD1ECD7126202AA00FA198C /* mouse.cpp */,
+ 8CD1ECD8126202AA00FA198C /* mouse.h */,
+ 8CD1ECD9126202AA00FA198C /* parser.cpp */,
+ 8CD1ECDA126202AA00FA198C /* parser.h */,
+ 8CD1ECDB126202AA00FA198C /* route.cpp */,
+ 8CD1ECDC126202AA00FA198C /* route.h */,
+ 8CD1ECDD126202AA00FA198C /* schedule.cpp */,
+ 8CD1ECDE126202AA00FA198C /* schedule.h */,
+ 8CD1ECDF126202AA00FA198C /* sound.cpp */,
+ 8CD1ECE0126202AA00FA198C /* sound.h */,
+ 8CD1ECE1126202AA00FA198C /* util.cpp */,
+ 8CD1ECE2126202AA00FA198C /* util.h */,
+ );
+ path = hugo;
+ sourceTree = "<group>";
+ };
+ 8CD1ECE3126202AA00FA198C /* toon */ = {
+ isa = PBXGroup;
+ children = (
+ 8CD1ECE4126202AA00FA198C /* anim.cpp */,
+ 8CD1ECE5126202AA00FA198C /* anim.h */,
+ 8CD1ECE6126202AA00FA198C /* audio.cpp */,
+ 8CD1ECE7126202AA00FA198C /* audio.h */,
+ 8CD1ECE8126202AA00FA198C /* character.cpp */,
+ 8CD1ECE9126202AA00FA198C /* character.h */,
+ 8CD1ECEA126202AA00FA198C /* conversation.cpp */,
+ 8CD1ECEB126202AA00FA198C /* conversation.h */,
+ 8CD1ECEC126202AA00FA198C /* detection.cpp */,
+ 8CD1ECED126202AA00FA198C /* drew.cpp */,
+ 8CD1ECEE126202AA00FA198C /* drew.h */,
+ 8CD1ECEF126202AA00FA198C /* flux.cpp */,
+ 8CD1ECF0126202AA00FA198C /* flux.h */,
+ 8CD1ECF1126202AA00FA198C /* font.cpp */,
+ 8CD1ECF2126202AA00FA198C /* font.h */,
+ 8CD1ECF3126202AA00FA198C /* hotspot.cpp */,
+ 8CD1ECF4126202AA00FA198C /* hotspot.h */,
+ 8CD1ECF7126202AA00FA198C /* movie.cpp */,
+ 8CD1ECF8126202AA00FA198C /* movie.h */,
+ 8CD1ECF9126202AA00FA198C /* path.cpp */,
+ 8CD1ECFA126202AA00FA198C /* path.h */,
+ 8CD1ECFB126202AA00FA198C /* picture.cpp */,
+ 8CD1ECFC126202AA00FA198C /* picture.h */,
+ 8CD1ECFD126202AA00FA198C /* resource.cpp */,
+ 8CD1ECFE126202AA00FA198C /* resource.h */,
+ 8CD1ECFF126202AA00FA198C /* script.cpp */,
+ 8CD1ED00126202AA00FA198C /* script.h */,
+ 8CD1ED01126202AA00FA198C /* script_func.cpp */,
+ 8CD1ED02126202AA00FA198C /* script_func.h */,
+ 8CD1ED03126202AA00FA198C /* state.cpp */,
+ 8CD1ED04126202AA00FA198C /* state.h */,
+ 8CD1ED05126202AA00FA198C /* text.cpp */,
+ 8CD1ED06126202AA00FA198C /* text.h */,
+ 8CD1ED07126202AA00FA198C /* tools.cpp */,
+ 8CD1ED08126202AA00FA198C /* tools.h */,
+ 8CD1ED09126202AA00FA198C /* toon.cpp */,
+ 8CD1ED0A126202AA00FA198C /* toon.h */,
+ );
+ path = toon;
+ sourceTree = "<group>";
+ };
+ 8CD80CBE1262729F001C6C87 /* teenagent */ = {
+ isa = PBXGroup;
+ children = (
+ 8CD80CBF1262729F001C6C87 /* actor.cpp */,
+ 8CD80CC01262729F001C6C87 /* actor.h */,
+ 8CD80CC11262729F001C6C87 /* animation.cpp */,
+ 8CD80CC21262729F001C6C87 /* animation.h */,
+ 8CD80CC31262729F001C6C87 /* callbacks.cpp */,
+ 8CD80CC41262729F001C6C87 /* console.cpp */,
+ 8CD80CC51262729F001C6C87 /* console.h */,
+ 8CD80CC61262729F001C6C87 /* detection.cpp */,
+ 8CD80CC71262729F001C6C87 /* dialog.cpp */,
+ 8CD80CC81262729F001C6C87 /* dialog.h */,
+ 8CD80CC91262729F001C6C87 /* font.cpp */,
+ 8CD80CCA1262729F001C6C87 /* font.h */,
+ 8CD80CCB1262729F001C6C87 /* inventory.cpp */,
+ 8CD80CCC1262729F001C6C87 /* inventory.h */,
+ 8CD80CCE1262729F001C6C87 /* music.cpp */,
+ 8CD80CCF1262729F001C6C87 /* music.h */,
+ 8CD80CD01262729F001C6C87 /* objects.cpp */,
+ 8CD80CD11262729F001C6C87 /* objects.h */,
+ 8CD80CD21262729F001C6C87 /* pack.cpp */,
+ 8CD80CD3126272A0001C6C87 /* pack.h */,
+ 8CD80CD4126272A0001C6C87 /* resources.cpp */,
+ 8CD80CD5126272A0001C6C87 /* resources.h */,
+ 8CD80CD6126272A0001C6C87 /* scene.cpp */,
+ 8CD80CD7126272A0001C6C87 /* scene.h */,
+ 8CD80CD8126272A0001C6C87 /* segment.cpp */,
+ 8CD80CD9126272A0001C6C87 /* segment.h */,
+ 8CD80CDA126272A0001C6C87 /* surface.cpp */,
+ 8CD80CDB126272A0001C6C87 /* surface.h */,
+ 8CD80CDC126272A0001C6C87 /* surface_list.cpp */,
+ 8CD80CDD126272A0001C6C87 /* surface_list.h */,
+ 8CD80CDE126272A0001C6C87 /* teenagent.cpp */,
+ 8CD80CDF126272A0001C6C87 /* teenagent.h */,
+ );
+ path = teenagent;
+ sourceTree = "<group>";
+ };
DF09422F0F63CB9A002D821E /* sdl */ = {
isa = PBXGroup;
children = (
@@ -4841,22 +5330,23 @@
DF2FFB940F485D950006E566 /* video */ = {
isa = PBXGroup;
children = (
- DFB0576211B753AF0015AE65 /* mpeg_player.cpp */,
- DFB0576311B753AF0015AE65 /* mpeg_player.h */,
- DFB0576411B753AF0015AE65 /* qt_decoder.cpp */,
- DFB0576511B753AF0015AE65 /* qt_decoder.h */,
- DFB0576611B753AF0015AE65 /* video_decoder.cpp */,
- DFB0576711B753AF0015AE65 /* video_decoder.h */,
- DF90EABF10B023F300C8F93F /* codecs */,
DF90EAB610B023D100C8F93F /* avi_decoder.cpp */,
DF90EAB710B023D100C8F93F /* avi_decoder.h */,
- DF61185B0FE3A9410042AD3F /* coktelvideo */,
+ DF90EABF10B023F300C8F93F /* codecs */,
+ DF895BFC124C24350077F6E8 /* coktel_decoder.cpp */,
+ DF895BFD124C24350077F6E8 /* coktel_decoder.h */,
DF6118600FE3A9410042AD3F /* dxa_decoder.cpp */,
DF6118610FE3A9410042AD3F /* dxa_decoder.h */,
DF6118620FE3A9410042AD3F /* flic_decoder.cpp */,
DF6118630FE3A9410042AD3F /* flic_decoder.h */,
+ DFB0576211B753AF0015AE65 /* mpeg_player.cpp */,
+ DFB0576311B753AF0015AE65 /* mpeg_player.h */,
+ DFB0576411B753AF0015AE65 /* qt_decoder.cpp */,
+ DFB0576511B753AF0015AE65 /* qt_decoder.h */,
DF6118640FE3A9410042AD3F /* smk_decoder.cpp */,
DF6118650FE3A9410042AD3F /* smk_decoder.h */,
+ DFB0576611B753AF0015AE65 /* video_decoder.cpp */,
+ DFB0576711B753AF0015AE65 /* video_decoder.h */,
);
path = video;
sourceTree = "<group>";
@@ -4926,8 +5416,6 @@
DF45B0EB116627D9009B85CC /* decoders */ = {
isa = PBXGroup;
children = (
- DF7F289E11FF24B000159131 /* mac_snd.cpp */,
- DF7F289F11FF24B000159131 /* mac_snd.h */,
DF45B0F7116627DA009B85CC /* adpcm.cpp */,
DF45B0F8116627DA009B85CC /* adpcm.h */,
DF45B0FA116627DA009B85CC /* aiff.cpp */,
@@ -4936,6 +5424,8 @@
DF45B0FE116627DA009B85CC /* flac.h */,
DF45B100116627DA009B85CC /* iff_sound.cpp */,
DF45B101116627DA009B85CC /* iff_sound.h */,
+ DF7F289E11FF24B000159131 /* mac_snd.cpp */,
+ DF7F289F11FF24B000159131 /* mac_snd.h */,
DF45B103116627DA009B85CC /* mp3.cpp */,
DF45B104116627DA009B85CC /* mp3.h */,
DF45B106116627DA009B85CC /* raw.cpp */,
@@ -4955,11 +5445,6 @@
DF45B175116628A5009B85CC /* graphics */ = {
isa = PBXGroup;
children = (
- DF7F285C11FF23B700159131 /* frameout.cpp */,
- DFB0578811B754570015AE65 /* maciconbar.cpp */,
- DFB0578911B754570015AE65 /* maciconbar.h */,
- DF9B924F118E46A00069C19D /* fontsjis.cpp */,
- DF9B9250118E46A00069C19D /* fontsjis.h */,
DF45B176116628A5009B85CC /* animate.cpp */,
DF45B177116628A5009B85CC /* animate.h */,
DF45B178116628A5009B85CC /* cache.cpp */,
@@ -4974,8 +5459,13 @@
DF45B181116628A5009B85CC /* cursor.h */,
DF45B182116628A5009B85CC /* font.cpp */,
DF45B183116628A5009B85CC /* font.h */,
+ DF9B924F118E46A00069C19D /* fontsjis.cpp */,
+ DF9B9250118E46A00069C19D /* fontsjis.h */,
+ DF7F285C11FF23B700159131 /* frameout.cpp */,
DF45B185116628A5009B85CC /* frameout.h */,
DF45B18A116628A5009B85CC /* helpers.h */,
+ DFB0578811B754570015AE65 /* maciconbar.cpp */,
+ DFB0578911B754570015AE65 /* maciconbar.h */,
DF45B18B116628A5009B85CC /* menu.cpp */,
DF45B18C116628A5009B85CC /* menu.h */,
DF45B18D116628A5009B85CC /* paint.cpp */,
@@ -5023,7 +5513,6 @@
DF45B1AC116628A5009B85CC /* audio.cpp */,
DF45B1AD116628A5009B85CC /* audio.h */,
DF45B1AE116628A5009B85CC /* drivers */,
- DF45B1B6116628A5009B85CC /* iterator */,
DF45B1BF116628A5009B85CC /* midiparser_sci.cpp */,
DF45B1C0116628A5009B85CC /* midiparser_sci.h */,
DF45B1C1116628A5009B85CC /* music.cpp */,
@@ -5037,6 +5526,7 @@
DF45B1AE116628A5009B85CC /* drivers */ = {
isa = PBXGroup;
children = (
+ DF0E30391252C5BD0082D593 /* cms.cpp */,
DF7F286011FF23D500159131 /* amigamac.cpp */,
DF45B1AF116628A5009B85CC /* adlib.cpp */,
DF45B1B1116628A5009B85CC /* fb01.cpp */,
@@ -5048,35 +5538,15 @@
path = drivers;
sourceTree = "<group>";
};
- DF45B1B6116628A5009B85CC /* iterator */ = {
- isa = PBXGroup;
- children = (
- );
- path = iterator;
- sourceTree = "<group>";
- };
DF45B1C5116628A5009B85CC /* video */ = {
isa = PBXGroup;
children = (
DF45B1C6116628A5009B85CC /* seq_decoder.cpp */,
DF45B1C7116628A5009B85CC /* seq_decoder.h */,
- DF45B1C8116628A5009B85CC /* vmd_decoder.cpp */,
- DF45B1C9116628A5009B85CC /* vmd_decoder.h */,
);
path = video;
sourceTree = "<group>";
};
- DF61185B0FE3A9410042AD3F /* coktelvideo */ = {
- isa = PBXGroup;
- children = (
- DF61185C0FE3A9410042AD3F /* coktelvideo.cpp */,
- DF61185D0FE3A9410042AD3F /* coktelvideo.h */,
- DF61185E0FE3A9410042AD3F /* indeo3.cpp */,
- DF61185F0FE3A9410042AD3F /* indeo3.h */,
- );
- path = coktelvideo;
- sourceTree = "<group>";
- };
DF6118780FE3A9AA0042AD3F /* save */ = {
isa = PBXGroup;
children = (
@@ -5105,14 +5575,8 @@
DF841FF50E7BA6A600F5680E /* engines */ = {
isa = PBXGroup;
children = (
- DFEC5D341166C67300C90552 /* savestate.cpp */,
- DFEC5D351166C67300C90552 /* savestate.h */,
- DF2FFD040F4870E50006E566 /* tucker */,
- DF2FFCBC0F4870690006E566 /* groovie */,
DF2FFC4C0F4863560006E566 /* advancedDetector.cpp */,
DF2FFC4D0F4863560006E566 /* advancedDetector.h */,
- DF7E8C510ED60067001CB19F /* game.cpp */,
- DF7E8C520ED60067001CB19F /* game.h */,
DF841FF60E7BA6A600F5680E /* agi */,
DF84202D0E7BA6A600F5680E /* agos */,
DF8420640E7BA6A600F5680E /* cine */,
@@ -5122,22 +5586,31 @@
DF8421040E7BA6A700F5680E /* drascula */,
DF8421140E7BA6A700F5680E /* engine.cpp */,
DF8421150E7BA6A700F5680E /* engine.h */,
+ DF7E8C510ED60067001CB19F /* game.cpp */,
+ DF7E8C520ED60067001CB19F /* game.h */,
DF8421170E7BA6A700F5680E /* gob */,
+ DF2FFCBC0F4870690006E566 /* groovie */,
+ 8CD1ECC5126202AA00FA198C /* hugo */,
DF8421A30E7BA6A800F5680E /* kyra */,
DF8422200E7BA6A800F5680E /* lure */,
DF84224E0E7BA6A800F5680E /* m4 */,
DF8422C90E7BA6A900F5680E /* made */,
DF8422E40E7BA6A900F5680E /* metaengine.h */,
- DFC830190F48AF18005EF03C /* sci */,
DF8422E60E7BA6A900F5680E /* parallaction */,
DF8423120E7BA6A900F5680E /* queen */,
DF84233E0E7BA6AA00F5680E /* saga */,
+ DFEC5D341166C67300C90552 /* savestate.cpp */,
+ DFEC5D351166C67300C90552 /* savestate.h */,
+ DFC830190F48AF18005EF03C /* sci */,
DF84237B0E7BA6AA00F5680E /* scumm */,
DF8424200E7BA6AB00F5680E /* sky */,
DF84244E0E7BA6AB00F5680E /* sword1 */,
DF8424770E7BA6AB00F5680E /* sword2 */,
+ 8CD80CBE1262729F001C6C87 /* teenagent */,
DF8424AA0E7BA6AB00F5680E /* tinsel */,
+ 8CD1ECE3126202AA00FA198C /* toon */,
DF8424FC0E7BA6AB00F5680E /* touche */,
+ DF2FFD040F4870E50006E566 /* tucker */,
);
name = engines;
path = ../../engines;
@@ -5146,17 +5619,6 @@
DF841FF60E7BA6A600F5680E /* agi */ = {
isa = PBXGroup;
children = (
- DF7F286F11FF243A00159131 /* detection_tables.h */,
- DF7F287011FF243A00159131 /* sound_2gs.cpp */,
- DF7F287111FF243A00159131 /* sound_2gs.h */,
- DF7F287211FF243B00159131 /* sound_coco3.cpp */,
- DF7F287311FF243B00159131 /* sound_coco3.h */,
- DF7F287411FF243B00159131 /* sound_midi.cpp */,
- DF7F287511FF243B00159131 /* sound_midi.h */,
- DF7F287611FF243B00159131 /* sound_pcjr.cpp */,
- DF7F287711FF243B00159131 /* sound_pcjr.h */,
- DF7F287811FF243B00159131 /* sound_sarien.cpp */,
- DF7F287911FF243B00159131 /* sound_sarien.h */,
DF841FF70E7BA6A600F5680E /* agi.cpp */,
DF841FF80E7BA6A600F5680E /* agi.h */,
DF841FF90E7BA6A600F5680E /* checks.cpp */,
@@ -5164,6 +5626,7 @@
DF841FFB0E7BA6A600F5680E /* console.h */,
DF841FFC0E7BA6A600F5680E /* cycle.cpp */,
DF841FFD0E7BA6A600F5680E /* detection.cpp */,
+ DF7F286F11FF243A00159131 /* detection_tables.h */,
DF841FFE0E7BA6A600F5680E /* font.h */,
DF841FFF0E7BA6A600F5680E /* global.cpp */,
DF8420000E7BA6A600F5680E /* graphics.cpp */,
@@ -5202,6 +5665,16 @@
DF8420220E7BA6A600F5680E /* saveload.cpp */,
DF8420230E7BA6A600F5680E /* sound.cpp */,
DF8420240E7BA6A600F5680E /* sound.h */,
+ DF7F287011FF243A00159131 /* sound_2gs.cpp */,
+ DF7F287111FF243A00159131 /* sound_2gs.h */,
+ DF7F287211FF243B00159131 /* sound_coco3.cpp */,
+ DF7F287311FF243B00159131 /* sound_coco3.h */,
+ DF7F287411FF243B00159131 /* sound_midi.cpp */,
+ DF7F287511FF243B00159131 /* sound_midi.h */,
+ DF7F287611FF243B00159131 /* sound_pcjr.cpp */,
+ DF7F287711FF243B00159131 /* sound_pcjr.h */,
+ DF7F287811FF243B00159131 /* sound_sarien.cpp */,
+ DF7F287911FF243B00159131 /* sound_sarien.h */,
DF8420250E7BA6A600F5680E /* sprite.cpp */,
DF8420260E7BA6A600F5680E /* sprite.h */,
DF8420270E7BA6A600F5680E /* text.cpp */,
@@ -5217,13 +5690,6 @@
DF84202D0E7BA6A600F5680E /* agos */ = {
isa = PBXGroup;
children = (
- DF6BF4D510529DE90069811F /* input_pn.cpp */,
- DF6BF4D610529DE90069811F /* string_pn.cpp */,
- DF6BF4D710529DE90069811F /* verb_pn.cpp */,
- DF6118AE0FE3A9EA0042AD3F /* feeble.cpp */,
- DF093E5C0F63CAD4002D821E /* pn.cpp */,
- DF093E5D0F63CAD4002D821E /* script_pn.cpp */,
- DF093E5E0F63CAD4002D821E /* vga_pn.cpp */,
DF84202E0E7BA6A600F5680E /* agos.cpp */,
DF84202F0E7BA6A600F5680E /* agos.h */,
DF8420300E7BA6A600F5680E /* animation.cpp */,
@@ -5240,9 +5706,11 @@
DF84203B0E7BA6A600F5680E /* detection_tables.h */,
DF84203C0E7BA6A600F5680E /* draw.cpp */,
DF84203D0E7BA6A600F5680E /* event.cpp */,
+ DF6118AE0FE3A9EA0042AD3F /* feeble.cpp */,
DF84203E0E7BA6A600F5680E /* gfx.cpp */,
DF84203F0E7BA6A600F5680E /* icons.cpp */,
DF8420400E7BA6A600F5680E /* input.cpp */,
+ DF6BF4D510529DE90069811F /* input_pn.cpp */,
DF8420410E7BA6A600F5680E /* intern.h */,
DF8420420E7BA6A600F5680E /* items.cpp */,
DF8420430E7BA6A600F5680E /* menus.cpp */,
@@ -5250,6 +5718,7 @@
DF8420450E7BA6A600F5680E /* midi.h */,
DF8420460E7BA6A600F5680E /* midiparser_s1d.cpp */,
DF8420480E7BA6A600F5680E /* oracle.cpp */,
+ DF093E5C0F63CAD4002D821E /* pn.cpp */,
DF8420490E7BA6A600F5680E /* res.cpp */,
DF84204A0E7BA6A600F5680E /* res_ami.cpp */,
DF84204B0E7BA6A600F5680E /* res_snd.cpp */,
@@ -5259,6 +5728,7 @@
DF84204F0E7BA6A600F5680E /* script_e1.cpp */,
DF8420500E7BA6A600F5680E /* script_e2.cpp */,
DF8420510E7BA6A600F5680E /* script_ff.cpp */,
+ DF093E5D0F63CAD4002D821E /* script_pn.cpp */,
DF8420520E7BA6A600F5680E /* script_pp.cpp */,
DF8420530E7BA6A600F5680E /* script_s1.cpp */,
DF8420540E7BA6A600F5680E /* script_s2.cpp */,
@@ -5266,12 +5736,15 @@
DF8420560E7BA6A600F5680E /* sound.cpp */,
DF8420570E7BA6A600F5680E /* sound.h */,
DF8420580E7BA6A600F5680E /* string.cpp */,
+ DF6BF4D610529DE90069811F /* string_pn.cpp */,
DF8420590E7BA6A600F5680E /* subroutine.cpp */,
DF84205A0E7BA6A600F5680E /* verb.cpp */,
+ DF6BF4D710529DE90069811F /* verb_pn.cpp */,
DF84205B0E7BA6A600F5680E /* vga.cpp */,
DF84205C0E7BA6A600F5680E /* vga.h */,
DF84205D0E7BA6A600F5680E /* vga_e2.cpp */,
DF84205E0E7BA6A600F5680E /* vga_ff.cpp */,
+ DF093E5E0F63CAD4002D821E /* vga_pn.cpp */,
DF84205F0E7BA6A600F5680E /* vga_s1.cpp */,
DF8420600E7BA6A600F5680E /* vga_s2.cpp */,
DF8420610E7BA6A600F5680E /* vga_ww.cpp */,
@@ -5284,8 +5757,6 @@
DF8420640E7BA6A600F5680E /* cine */ = {
isa = PBXGroup;
children = (
- DFAAAFF50F0112AD003E9390 /* saveload.cpp */,
- DFAAAFF60F0112AD003E9390 /* saveload.h */,
DF8420650E7BA6A600F5680E /* anim.cpp */,
DF8420660E7BA6A600F5680E /* anim.h */,
DF8420670E7BA6A600F5680E /* bg.cpp */,
@@ -5311,6 +5782,8 @@
DF84207C0E7BA6A600F5680E /* prc.h */,
DF84207D0E7BA6A600F5680E /* rel.cpp */,
DF84207E0E7BA6A600F5680E /* rel.h */,
+ DFAAAFF50F0112AD003E9390 /* saveload.cpp */,
+ DFAAAFF60F0112AD003E9390 /* saveload.h */,
DF84207F0E7BA6A600F5680E /* script.h */,
DF8420800E7BA6A600F5680E /* script_fw.cpp */,
DF8420810E7BA6A600F5680E /* script_os.cpp */,
@@ -5329,12 +5802,6 @@
DF84208B0E7BA6A600F5680E /* cruise */ = {
isa = PBXGroup;
children = (
- DFE88C440F874A1100C555C5 /* sound.cpp */,
- DFE88C450F874A1100C555C5 /* sound.h */,
- DF2FFC700F4867910006E566 /* debugger.cpp */,
- DF2FFC710F4867910006E566 /* debugger.h */,
- DF2FFC720F4867910006E566 /* staticres.cpp */,
- DF2FFC730F4867910006E566 /* staticres.h */,
DF8420AB0E7BA6A600F5680E /* actor.cpp */,
DF8420AC0E7BA6A600F5680E /* actor.h */,
DF8420AE0E7BA6A600F5680E /* background.cpp */,
@@ -5351,6 +5818,8 @@
DF8420BE0E7BA6A700F5680E /* ctp.h */,
DF8420C00E7BA6A700F5680E /* dataLoader.cpp */,
DF8420C10E7BA6A700F5680E /* dataLoader.h */,
+ DF2FFC700F4867910006E566 /* debugger.cpp */,
+ DF2FFC710F4867910006E566 /* debugger.h */,
DF8420C30E7BA6A700F5680E /* decompiler.cpp */,
DF8420C50E7BA6A700F5680E /* delphine-unpack.cpp */,
DF8420C70E7BA6A700F5680E /* detection.cpp */,
@@ -5380,8 +5849,12 @@
DF8420F00E7BA6A700F5680E /* saveload.h */,
DF8420F20E7BA6A700F5680E /* script.cpp */,
DF8420F30E7BA6A700F5680E /* script.h */,
+ DFE88C440F874A1100C555C5 /* sound.cpp */,
+ DFE88C450F874A1100C555C5 /* sound.h */,
DF8420F50E7BA6A700F5680E /* stack.cpp */,
DF8420F60E7BA6A700F5680E /* stack.h */,
+ DF2FFC720F4867910006E566 /* staticres.cpp */,
+ DF2FFC730F4867910006E566 /* staticres.h */,
DF8420F90E7BA6A700F5680E /* various.cpp */,
DF8420FA0E7BA6A700F5680E /* various.h */,
DF8420FC0E7BA6A700F5680E /* vars.cpp */,
@@ -5395,11 +5868,10 @@
DF8421040E7BA6A700F5680E /* drascula */ = {
isa = PBXGroup;
children = (
- DF7F28A311FF24C400159131 /* console.cpp */,
- DF7F28A411FF24C400159131 /* console.h */,
- DFCDC6F611662AAB00A7D2A0 /* resource.cpp */,
DF8421050E7BA6A700F5680E /* actors.cpp */,
DF8421060E7BA6A700F5680E /* animation.cpp */,
+ DF7F28A311FF24C400159131 /* console.cpp */,
+ DF7F28A411FF24C400159131 /* console.h */,
DF8421070E7BA6A700F5680E /* converse.cpp */,
DF8421080E7BA6A700F5680E /* detection.cpp */,
DF8421090E7BA6A700F5680E /* drascula.cpp */,
@@ -5408,6 +5880,7 @@
DF84210C0E7BA6A700F5680E /* interface.cpp */,
DF84210E0E7BA6A700F5680E /* objects.cpp */,
DF84210F0E7BA6A700F5680E /* palette.cpp */,
+ DFCDC6F611662AAB00A7D2A0 /* resource.cpp */,
DF8421100E7BA6A700F5680E /* rooms.cpp */,
DF8421110E7BA6A700F5680E /* saveload.cpp */,
DF8421120E7BA6A700F5680E /* sound.cpp */,
@@ -5419,35 +5892,20 @@
DF8421170E7BA6A700F5680E /* gob */ = {
isa = PBXGroup;
children = (
- DF90EAA310B0234300C8F93F /* draw_playtoons.cpp */,
- DF6BF4E810529E6E0069811F /* init_v4.cpp */,
- DF6BF4E910529E6E0069811F /* inter_playtoons.cpp */,
- DF7585C3100CB66E00CC3324 /* expression.cpp */,
- DF7585C4100CB66E00CC3324 /* expression.h */,
- DF7585C5100CB66E00CC3324 /* hotspots.cpp */,
- DF7585C6100CB66E00CC3324 /* hotspots.h */,
- DF7585C7100CB66E00CC3324 /* init_v6.cpp */,
- DF7585C8100CB66E00CC3324 /* resources.cpp */,
- DF7585C9100CB66E00CC3324 /* resources.h */,
- DF7585CA100CB66E00CC3324 /* script.cpp */,
- DF7585CB100CB66E00CC3324 /* script.h */,
- DF7585CC100CB66E00CC3324 /* totfile.cpp */,
- DF7585CD100CB66E00CC3324 /* totfile.h */,
- DF6118780FE3A9AA0042AD3F /* save */,
- DF6118590FE3A9020042AD3F /* helper.h */,
- DF09CC060FAC4E1900A5AFD7 /* demos */,
- DF09CC0D0FAC4E1900A5AFD7 /* draw_fascin.cpp */,
- DF09CC0F0FAC4E1900A5AFD7 /* inter_fascin.cpp */,
DF84211B0E7BA6A700F5680E /* dataio.cpp */,
DF84211C0E7BA6A700F5680E /* dataio.h */,
+ DF09CC060FAC4E1900A5AFD7 /* demos */,
DF84211D0E7BA6A700F5680E /* detection.cpp */,
+ DF895C23124C25150077F6E8 /* detection_tables.h */,
DF84211E0E7BA6A700F5680E /* draw.cpp */,
DF84211F0E7BA6A700F5680E /* draw.h */,
DF8421200E7BA6A700F5680E /* draw_bargon.cpp */,
+ DF09CC0D0FAC4E1900A5AFD7 /* draw_fascin.cpp */,
+ DF90EAA310B0234300C8F93F /* draw_playtoons.cpp */,
DF8421210E7BA6A700F5680E /* draw_v1.cpp */,
DF8421220E7BA6A700F5680E /* draw_v2.cpp */,
- DF8421230E7BA6A700F5680E /* driver_vga.cpp */,
- DF8421240E7BA6A700F5680E /* driver_vga.h */,
+ DF7585C3100CB66E00CC3324 /* expression.cpp */,
+ DF7585C4100CB66E00CC3324 /* expression.h */,
DF8421250E7BA6A700F5680E /* game.cpp */,
DF8421260E7BA6A700F5680E /* game.h */,
DF8421290E7BA6A700F5680E /* global.cpp */,
@@ -5460,14 +5918,22 @@
DF8421300E7BA6A700F5680E /* goblin_v2.cpp */,
DF8421310E7BA6A700F5680E /* goblin_v3.cpp */,
DF8421320E7BA6A700F5680E /* goblin_v4.cpp */,
+ DF6118590FE3A9020042AD3F /* helper.h */,
+ DF7585C5100CB66E00CC3324 /* hotspots.cpp */,
+ DF7585C6100CB66E00CC3324 /* hotspots.h */,
DF8421330E7BA6A700F5680E /* init.cpp */,
DF8421340E7BA6A700F5680E /* init.h */,
+ DF895C24124C25150077F6E8 /* init_fascin.cpp */,
DF8421350E7BA6A700F5680E /* init_v1.cpp */,
DF8421360E7BA6A700F5680E /* init_v2.cpp */,
DF8421370E7BA6A700F5680E /* init_v3.cpp */,
+ DF6BF4E810529E6E0069811F /* init_v4.cpp */,
+ DF7585C7100CB66E00CC3324 /* init_v6.cpp */,
DF8421380E7BA6A700F5680E /* inter.cpp */,
DF8421390E7BA6A700F5680E /* inter.h */,
DF84213A0E7BA6A700F5680E /* inter_bargon.cpp */,
+ DF09CC0F0FAC4E1900A5AFD7 /* inter_fascin.cpp */,
+ DF6BF4E910529E6E0069811F /* inter_playtoons.cpp */,
DF84213B0E7BA6A700F5680E /* inter_v1.cpp */,
DF84213C0E7BA6A700F5680E /* inter_v2.cpp */,
DF84213D0E7BA6A700F5680E /* inter_v3.cpp */,
@@ -5484,11 +5950,20 @@
DF84214A0E7BA6A700F5680E /* mult_v2.cpp */,
DF84214C0E7BA6A700F5680E /* palanim.cpp */,
DF84214D0E7BA6A700F5680E /* palanim.h */,
+ DF7585C8100CB66E00CC3324 /* resources.cpp */,
+ DF7585C9100CB66E00CC3324 /* resources.h */,
+ DF6118780FE3A9AA0042AD3F /* save */,
DF8421570E7BA6A700F5680E /* scenery.cpp */,
DF8421580E7BA6A700F5680E /* scenery.h */,
DF8421590E7BA6A700F5680E /* scenery_v1.cpp */,
DF84215A0E7BA6A700F5680E /* scenery_v2.cpp */,
+ DF7585CA100CB66E00CC3324 /* script.cpp */,
+ DF7585CB100CB66E00CC3324 /* script.h */,
DF84215B0E7BA6A700F5680E /* sound */,
+ 8CD80C89126271A9001C6C87 /* surface.cpp */,
+ 8CD80C8A126271A9001C6C87 /* surface.h */,
+ DF7585CC100CB66E00CC3324 /* totfile.cpp */,
+ DF7585CD100CB66E00CC3324 /* totfile.h */,
DF84216F0E7BA6A700F5680E /* util.cpp */,
DF8421700E7BA6A700F5680E /* util.h */,
DF8421710E7BA6A700F5680E /* variables.cpp */,
@@ -5534,29 +6009,11 @@
DF8421A30E7BA6A800F5680E /* kyra */ = {
isa = PBXGroup;
children = (
- DF2EC3FD10E64C4300765801 /* animator_tim.cpp */,
- DF6BF4E210529E260069811F /* sound_adlib.h */,
- DF6BF4E310529E260069811F /* sound_amiga.cpp */,
- DF6118B30FE3AA280042AD3F /* saveload_lol.cpp */,
- DF6118B40FE3AA280042AD3F /* sound_lol.cpp */,
- DF6118B50FE3AA280042AD3F /* sound_pcspk.cpp */,
- DF6118B60FE3AA280042AD3F /* text_lol.cpp */,
- DF6118B70FE3AA280042AD3F /* text_lol.h */,
- DF89C2870F62D55C00D756B6 /* sprites_lol.cpp */,
- DF573CBD0F5A85E100961A72 /* timer_lol.cpp */,
- DF2FFC490F4863100006E566 /* scene_lol.cpp */,
- DF2FFC2E0F48628A0006E566 /* gui_lol.cpp */,
- DF2FFC2F0F48628A0006E566 /* gui_lol.h */,
- DF2FFC300F48628A0006E566 /* items_lol.cpp */,
- DF2FFC310F48628A0006E566 /* script_lol.cpp */,
- DF2FFC320F48628A0006E566 /* sequences_lol.cpp */,
- DF2FFC330F48628A0006E566 /* sound_midi.cpp */,
- DF2FFC360F48628A0006E566 /* util.cpp */,
- DF2FFC370F48628A0006E566 /* util.h */,
DF8421A40E7BA6A800F5680E /* animator_hof.cpp */,
DF8421A50E7BA6A800F5680E /* animator_lok.cpp */,
DF8421A60E7BA6A800F5680E /* animator_lok.h */,
DF8421A70E7BA6A800F5680E /* animator_mr.cpp */,
+ DF2EC3FD10E64C4300765801 /* animator_tim.cpp */,
DF8421A90E7BA6A800F5680E /* animator_v2.cpp */,
DF8421AB0E7BA6A800F5680E /* debugger.cpp */,
DF8421AC0E7BA6A800F5680E /* debugger.h */,
@@ -5567,12 +6024,15 @@
DF8421B10E7BA6A800F5680E /* gui_hof.h */,
DF8421B20E7BA6A800F5680E /* gui_lok.cpp */,
DF8421B30E7BA6A800F5680E /* gui_lok.h */,
+ DF2FFC2E0F48628A0006E566 /* gui_lol.cpp */,
+ DF2FFC2F0F48628A0006E566 /* gui_lol.h */,
DF8421B40E7BA6A800F5680E /* gui_mr.cpp */,
DF8421B50E7BA6A800F5680E /* gui_mr.h */,
DF8421B70E7BA6A800F5680E /* gui_v2.cpp */,
DF8421B80E7BA6A800F5680E /* gui_v2.h */,
DF8421BA0E7BA6A800F5680E /* items_hof.cpp */,
DF8421BB0E7BA6A800F5680E /* items_lok.cpp */,
+ DF2FFC300F48628A0006E566 /* items_lol.cpp */,
DF8421BC0E7BA6A800F5680E /* items_mr.cpp */,
DF8421BE0E7BA6A800F5680E /* items_v2.cpp */,
DF8421C10E7BA6A800F5680E /* kyra_hof.cpp */,
@@ -5594,9 +6054,11 @@
DF8421D30E7BA6A800F5680E /* saveload.cpp */,
DF8421D40E7BA6A800F5680E /* saveload_hof.cpp */,
DF8421D50E7BA6A800F5680E /* saveload_lok.cpp */,
+ DF6118B30FE3AA280042AD3F /* saveload_lol.cpp */,
DF8421D60E7BA6A800F5680E /* saveload_mr.cpp */,
DF8421DA0E7BA6A800F5680E /* scene_hof.cpp */,
DF8421DB0E7BA6A800F5680E /* scene_lok.cpp */,
+ DF2FFC490F4863100006E566 /* scene_lol.cpp */,
DF8421DC0E7BA6A800F5680E /* scene_mr.cpp */,
DF8421DD0E7BA6A800F5680E /* scene_v1.cpp */,
DF8421DE0E7BA6A800F5680E /* scene_v2.cpp */,
@@ -5616,6 +6078,7 @@
DF8421EF0E7BA6A800F5680E /* script.h */,
DF8421F00E7BA6A800F5680E /* script_hof.cpp */,
DF8421F10E7BA6A800F5680E /* script_lok.cpp */,
+ DF2FFC310F48628A0006E566 /* script_lol.cpp */,
DF8421F20E7BA6A800F5680E /* script_mr.cpp */,
DF8421F30E7BA6A800F5680E /* script_tim.cpp */,
DF8421F40E7BA6A800F5680E /* script_tim.h */,
@@ -5625,29 +6088,41 @@
DF8421F90E7BA6A800F5680E /* seqplayer.h */,
DF8421FA0E7BA6A800F5680E /* sequences_hof.cpp */,
DF8421FB0E7BA6A800F5680E /* sequences_lok.cpp */,
+ DF2FFC320F48628A0006E566 /* sequences_lol.cpp */,
DF8421FC0E7BA6A800F5680E /* sequences_mr.cpp */,
DF8421FE0E7BA6A800F5680E /* sequences_v2.cpp */,
DF8422000E7BA6A800F5680E /* sound.cpp */,
DF8422010E7BA6A800F5680E /* sound.h */,
DF8422020E7BA6A800F5680E /* sound_adlib.cpp */,
+ DF6BF4E210529E260069811F /* sound_adlib.h */,
+ DF6BF4E310529E260069811F /* sound_amiga.cpp */,
DF8422030E7BA6A800F5680E /* sound_digital.cpp */,
DF8422040E7BA6A800F5680E /* sound_lok.cpp */,
+ DF6118B40FE3AA280042AD3F /* sound_lol.cpp */,
+ DF2FFC330F48628A0006E566 /* sound_midi.cpp */,
+ DF6118B50FE3AA280042AD3F /* sound_pcspk.cpp */,
DF8422050E7BA6A800F5680E /* sound_towns.cpp */,
DF8422070E7BA6A800F5680E /* sprites.cpp */,
DF8422080E7BA6A800F5680E /* sprites.h */,
+ DF89C2870F62D55C00D756B6 /* sprites_lol.cpp */,
DF8422090E7BA6A800F5680E /* staticres.cpp */,
DF84220A0E7BA6A800F5680E /* text.cpp */,
DF84220B0E7BA6A800F5680E /* text.h */,
DF84220C0E7BA6A800F5680E /* text_hof.cpp */,
DF84220D0E7BA6A800F5680E /* text_hof.h */,
DF84220E0E7BA6A800F5680E /* text_lok.cpp */,
+ DF6118B60FE3AA280042AD3F /* text_lol.cpp */,
+ DF6118B70FE3AA280042AD3F /* text_lol.h */,
DF84220F0E7BA6A800F5680E /* text_mr.cpp */,
DF8422100E7BA6A800F5680E /* text_mr.h */,
DF8422140E7BA6A800F5680E /* timer.cpp */,
DF8422150E7BA6A800F5680E /* timer.h */,
DF8422160E7BA6A800F5680E /* timer_hof.cpp */,
DF8422170E7BA6A800F5680E /* timer_lok.cpp */,
+ DF573CBD0F5A85E100961A72 /* timer_lol.cpp */,
DF8422180E7BA6A800F5680E /* timer_mr.cpp */,
+ DF2FFC360F48628A0006E566 /* util.cpp */,
+ DF2FFC370F48628A0006E566 /* util.h */,
DF84221C0E7BA6A800F5680E /* vqa.cpp */,
DF84221D0E7BA6A800F5680E /* vqa.h */,
DF84221E0E7BA6A800F5680E /* wsamovie.cpp */,
@@ -5710,10 +6185,6 @@
DF84224E0E7BA6A800F5680E /* m4 */ = {
isa = PBXGroup;
children = (
- DF2EC3F610E64C0C00765801 /* dialogs.cpp */,
- DF2EC3F710E64C0C00765801 /* dialogs.h */,
- DF90EAAB10B0236F00C8F93F /* staticres.cpp */,
- DF90EAAC10B0236F00C8F93F /* staticres.h */,
DF84226E0E7BA6A800F5680E /* actor.cpp */,
DF84226F0E7BA6A800F5680E /* actor.h */,
DF8422710E7BA6A800F5680E /* animation.cpp */,
@@ -5728,6 +6199,8 @@
DF84227E0E7BA6A900F5680E /* converse.cpp */,
DF84227F0E7BA6A900F5680E /* converse.h */,
DF8422810E7BA6A900F5680E /* detection.cpp */,
+ DF2EC3F610E64C0C00765801 /* dialogs.cpp */,
+ DF2EC3F710E64C0C00765801 /* dialogs.h */,
DF8422830E7BA6A900F5680E /* events.cpp */,
DF8422840E7BA6A900F5680E /* events.h */,
DF8422860E7BA6A900F5680E /* font.cpp */,
@@ -5744,12 +6217,22 @@
DF8422970E7BA6A900F5680E /* m4.h */,
DF8422990E7BA6A900F5680E /* m4_menus.cpp */,
DF84229A0E7BA6A900F5680E /* m4_menus.h */,
+ 8CB5A9B71253FD6800CB6BC7 /* m4_scene.cpp */,
+ 8CB5A9B81253FD6800CB6BC7 /* m4_scene.h */,
DF84229C0E7BA6A900F5680E /* m4_views.cpp */,
DF84229D0E7BA6A900F5680E /* m4_views.h */,
DF84229F0E7BA6A900F5680E /* mads_anim.cpp */,
DF8422A00E7BA6A900F5680E /* mads_anim.h */,
+ 8CB5A9B91253FD6800CB6BC7 /* mads_logic.cpp */,
+ 8CB5A9BA1253FD6800CB6BC7 /* mads_logic.h */,
DF8422A20E7BA6A900F5680E /* mads_menus.cpp */,
DF8422A30E7BA6A900F5680E /* mads_menus.h */,
+ 8CB5A9BB1253FD6800CB6BC7 /* mads_player.cpp */,
+ 8CB5A9BC1253FD6800CB6BC7 /* mads_player.h */,
+ 8CB5A9BD1253FD6800CB6BC7 /* mads_scene.cpp */,
+ 8CB5A9BE1253FD6800CB6BC7 /* mads_scene.h */,
+ 8CB5A9BF1253FD6900CB6BC7 /* mads_views.cpp */,
+ 8CB5A9C01253FD6900CB6BC7 /* mads_views.h */,
DF8422A50E7BA6A900F5680E /* midi.cpp */,
DF8422A60E7BA6A900F5680E /* midi.h */,
DF8422A90E7BA6A900F5680E /* rails.cpp */,
@@ -5767,6 +6250,8 @@
DF8422BA0E7BA6A900F5680E /* sound.h */,
DF8422BC0E7BA6A900F5680E /* sprite.cpp */,
DF8422BD0E7BA6A900F5680E /* sprite.h */,
+ DF90EAAB10B0236F00C8F93F /* staticres.cpp */,
+ DF90EAAC10B0236F00C8F93F /* staticres.h */,
DF8422BF0E7BA6A900F5680E /* viewmgr.cpp */,
DF8422C00E7BA6A900F5680E /* viewmgr.h */,
DF8422C20E7BA6A900F5680E /* woodscript.cpp */,
@@ -5812,10 +6297,6 @@
DF8422E60E7BA6A900F5680E /* parallaction */ = {
isa = PBXGroup;
children = (
- DF6118540FE3A8990042AD3F /* disk.cpp */,
- DF5CEB260F75535000DEA624 /* sound_br.cpp */,
- DF5CEB270F75535000DEA624 /* sound_ns.cpp */,
- DF573CBA0F5A85B300961A72 /* exec.cpp */,
DF8422E70E7BA6A900F5680E /* balloons.cpp */,
DF8422E80E7BA6A900F5680E /* callables_br.cpp */,
DF8422E90E7BA6A900F5680E /* callables_ns.cpp */,
@@ -5823,9 +6304,11 @@
DF8422EB0E7BA6A900F5680E /* debug.h */,
DF8422EC0E7BA6A900F5680E /* detection.cpp */,
DF8422ED0E7BA6A900F5680E /* dialogue.cpp */,
+ DF6118540FE3A8990042AD3F /* disk.cpp */,
DF8422EE0E7BA6A900F5680E /* disk.h */,
DF8422EF0E7BA6A900F5680E /* disk_br.cpp */,
DF8422F00E7BA6A900F5680E /* disk_ns.cpp */,
+ DF573CBA0F5A85B300961A72 /* exec.cpp */,
DF8422F10E7BA6A900F5680E /* exec.h */,
DF8422F20E7BA6A900F5680E /* exec_br.cpp */,
DF8422F30E7BA6A900F5680E /* exec_ns.cpp */,
@@ -5854,6 +6337,8 @@
DF84230B0E7BA6A900F5680E /* saveload.cpp */,
DF84230C0E7BA6A900F5680E /* saveload.h */,
DF84230E0E7BA6A900F5680E /* sound.h */,
+ DF5CEB260F75535000DEA624 /* sound_br.cpp */,
+ DF5CEB270F75535000DEA624 /* sound_ns.cpp */,
DF84230F0E7BA6A900F5680E /* staticres.cpp */,
DF8423100E7BA6A900F5680E /* walk.cpp */,
DF8423110E7BA6A900F5680E /* walk.h */,
@@ -5912,12 +6397,6 @@
DF84233E0E7BA6AA00F5680E /* saga */ = {
isa = PBXGroup;
children = (
- DF2FFC600F48672D0006E566 /* resource.cpp */,
- DF2FFC610F48672D0006E566 /* resource.h */,
- DF2FFC620F48672D0006E566 /* resource_hrs.cpp */,
- DF2FFC630F48672D0006E566 /* resource_res.cpp */,
- DF2FFC640F48672D0006E566 /* resource_rsc.cpp */,
- DF2FFC650F48672D0006E566 /* sfuncs_ihnm.cpp */,
DF84233F0E7BA6AA00F5680E /* actor.cpp */,
DF8423400E7BA6AA00F5680E /* actor.h */,
DF8423410E7BA6AA00F5680E /* actor_path.cpp */,
@@ -5956,6 +6435,11 @@
DF8423660E7BA6AA00F5680E /* puzzle.h */,
DF8423670E7BA6AA00F5680E /* render.cpp */,
DF8423680E7BA6AA00F5680E /* render.h */,
+ DF2FFC600F48672D0006E566 /* resource.cpp */,
+ DF2FFC610F48672D0006E566 /* resource.h */,
+ DF2FFC620F48672D0006E566 /* resource_hrs.cpp */,
+ DF2FFC630F48672D0006E566 /* resource_res.cpp */,
+ DF2FFC640F48672D0006E566 /* resource_rsc.cpp */,
DF84236B0E7BA6AA00F5680E /* saga.cpp */,
DF84236C0E7BA6AA00F5680E /* saga.h */,
DF84236D0E7BA6AA00F5680E /* saveload.cpp */,
@@ -5964,6 +6448,7 @@
DF8423700E7BA6AA00F5680E /* script.cpp */,
DF8423710E7BA6AA00F5680E /* script.h */,
DF8423720E7BA6AA00F5680E /* sfuncs.cpp */,
+ DF2FFC650F48672D0006E566 /* sfuncs_ihnm.cpp */,
DF8423730E7BA6AA00F5680E /* sndres.cpp */,
DF8423740E7BA6AA00F5680E /* sndres.h */,
DF8423750E7BA6AA00F5680E /* sound.cpp */,
@@ -5978,23 +6463,6 @@
DF84237B0E7BA6AA00F5680E /* scumm */ = {
isa = PBXGroup;
children = (
- DF2EC4FD10E64D7C00765801 /* player_pce.cpp */,
- DF2EC4FE10E64D7C00765801 /* player_pce.h */,
- DF2EC4FF10E64D7C00765801 /* player_sid.cpp */,
- DF2EC50010E64D7C00765801 /* player_sid.h */,
- DF6BF4F110529EE40069811F /* player_v4a.cpp */,
- DF6BF4F210529EE40069811F /* player_v4a.h */,
- DF6118C60FE3AABD0042AD3F /* player_v2cms.cpp */,
- DF09CC260FAC4EAB00A5AFD7 /* script_v3.cpp */,
- DF09CC270FAC4EAB00A5AFD7 /* script_v4.cpp */,
- DF09CC1D0FAC4E6200A5AFD7 /* scumm_v0.h */,
- DF09CC1E0FAC4E6200A5AFD7 /* scumm_v2.h */,
- DF09CC1F0FAC4E6200A5AFD7 /* scumm_v3.h */,
- DF09CC200FAC4E6200A5AFD7 /* scumm_v4.h */,
- DF09CC210FAC4E6200A5AFD7 /* scumm_v5.h */,
- DF09CC220FAC4E6200A5AFD7 /* scumm_v6.h */,
- DF09CC230FAC4E6200A5AFD7 /* scumm_v7.h */,
- DF09CC240FAC4E6200A5AFD7 /* scumm_v8.h */,
DF84237C0E7BA6AA00F5680E /* actor.cpp */,
DF84237D0E7BA6AA00F5680E /* actor.h */,
DF84237E0E7BA6AA00F5680E /* akos.cpp */,
@@ -6025,6 +6493,7 @@
DF8423970E7BA6AA00F5680E /* file_nes.h */,
DF8423980E7BA6AA00F5680E /* gfx.cpp */,
DF8423990E7BA6AA00F5680E /* gfx.h */,
+ 8CD80C90126271BD001C6C87 /* gfx_towns.cpp */,
DF84239B0E7BA6AA00F5680E /* he */,
DF8423B50E7BA6AA00F5680E /* help.cpp */,
DF8423B60E7BA6AA00F5680E /* help.h */,
@@ -6032,7 +6501,6 @@
DF8423C20E7BA6AA00F5680E /* imuse_digi */,
DF8423D00E7BA6AA00F5680E /* input.cpp */,
DF8423D10E7BA6AA00F5680E /* insane */,
- DF8423D90E7BA6AA00F5680E /* midiparser_eup.cpp */,
DF8423DA0E7BA6AA00F5680E /* midiparser_ro.cpp */,
DF8423DC0E7BA6AA00F5680E /* music.h */,
DF8423DD0E7BA6AA00F5680E /* nut_renderer.cpp */,
@@ -6044,14 +6512,23 @@
DF8423E30E7BA6AA00F5680E /* player_mod.h */,
DF8423E40E7BA6AA00F5680E /* player_nes.cpp */,
DF8423E50E7BA6AA00F5680E /* player_nes.h */,
+ DF2EC4FD10E64D7C00765801 /* player_pce.cpp */,
+ DF2EC4FE10E64D7C00765801 /* player_pce.h */,
+ DF2EC4FF10E64D7C00765801 /* player_sid.cpp */,
+ DF2EC50010E64D7C00765801 /* player_sid.h */,
+ DF895C01124C24680077F6E8 /* player_towns.cpp */,
+ DF895C02124C24680077F6E8 /* player_towns.h */,
DF8423E60E7BA6AA00F5680E /* player_v1.cpp */,
DF8423E70E7BA6AA00F5680E /* player_v1.h */,
DF8423E80E7BA6AA00F5680E /* player_v2.cpp */,
DF8423E90E7BA6AA00F5680E /* player_v2.h */,
DF8423EA0E7BA6AA00F5680E /* player_v2a.cpp */,
DF8423EB0E7BA6AA00F5680E /* player_v2a.h */,
+ DF6118C60FE3AABD0042AD3F /* player_v2cms.cpp */,
DF8423EC0E7BA6AA00F5680E /* player_v3a.cpp */,
DF8423ED0E7BA6AA00F5680E /* player_v3a.h */,
+ DF6BF4F110529EE40069811F /* player_v4a.cpp */,
+ DF6BF4F210529EE40069811F /* player_v4a.h */,
DF8423EF0E7BA6AA00F5680E /* resource.cpp */,
DF8423F00E7BA6AA00F5680E /* resource.h */,
DF8423F10E7BA6AA00F5680E /* resource_v2.cpp */,
@@ -6064,12 +6541,22 @@
DF8423F80E7BA6AA00F5680E /* script.h */,
DF8423F90E7BA6AA00F5680E /* script_v0.cpp */,
DF8423FA0E7BA6AA00F5680E /* script_v2.cpp */,
+ DF09CC260FAC4EAB00A5AFD7 /* script_v3.cpp */,
+ DF09CC270FAC4EAB00A5AFD7 /* script_v4.cpp */,
DF8423FB0E7BA6AA00F5680E /* script_v5.cpp */,
DF8423FC0E7BA6AA00F5680E /* script_v6.cpp */,
DF8423FD0E7BA6AA00F5680E /* script_v8.cpp */,
DF8423FE0E7BA6AA00F5680E /* scumm-md5.h */,
DF8423FF0E7BA6AA00F5680E /* scumm.cpp */,
DF8424000E7BA6AA00F5680E /* scumm.h */,
+ DF09CC1D0FAC4E6200A5AFD7 /* scumm_v0.h */,
+ DF09CC1E0FAC4E6200A5AFD7 /* scumm_v2.h */,
+ DF09CC1F0FAC4E6200A5AFD7 /* scumm_v3.h */,
+ DF09CC200FAC4E6200A5AFD7 /* scumm_v4.h */,
+ DF09CC210FAC4E6200A5AFD7 /* scumm_v5.h */,
+ DF09CC220FAC4E6200A5AFD7 /* scumm_v6.h */,
+ DF09CC230FAC4E6200A5AFD7 /* scumm_v7.h */,
+ DF09CC240FAC4E6200A5AFD7 /* scumm_v8.h */,
DF8424010E7BA6AA00F5680E /* smush */,
DF8424150E7BA6AB00F5680E /* sound.cpp */,
DF8424160E7BA6AB00F5680E /* sound.h */,
@@ -6192,7 +6679,6 @@
DF8424200E7BA6AB00F5680E /* sky */ = {
isa = PBXGroup;
children = (
- DFAAAFF80F0112C1003E9390 /* detection.cpp */,
DF8424210E7BA6AB00F5680E /* autoroute.cpp */,
DF8424220E7BA6AB00F5680E /* autoroute.h */,
DF8424230E7BA6AB00F5680E /* compact.cpp */,
@@ -6201,6 +6687,7 @@
DF8424260E7BA6AB00F5680E /* control.h */,
DF8424270E7BA6AB00F5680E /* debug.cpp */,
DF8424280E7BA6AB00F5680E /* debug.h */,
+ DFAAAFF80F0112C1003E9390 /* detection.cpp */,
DF8424290E7BA6AB00F5680E /* disk.cpp */,
DF84242A0E7BA6AB00F5680E /* disk.h */,
DF84242B0E7BA6AB00F5680E /* grid.cpp */,
@@ -6251,7 +6738,6 @@
DF84244E0E7BA6AB00F5680E /* sword1 */ = {
isa = PBXGroup;
children = (
- DFAAAFFB0F0112DF003E9390 /* detection.cpp */,
DF84244F0E7BA6AB00F5680E /* animation.cpp */,
DF8424500E7BA6AB00F5680E /* animation.h */,
DF8424510E7BA6AB00F5680E /* collision.h */,
@@ -6259,6 +6745,7 @@
DF8424530E7BA6AB00F5680E /* control.h */,
DF8424560E7BA6AB00F5680E /* debug.cpp */,
DF8424570E7BA6AB00F5680E /* debug.h */,
+ DFAAAFFB0F0112DF003E9390 /* detection.cpp */,
DF8424580E7BA6AB00F5680E /* eventman.cpp */,
DF8424590E7BA6AB00F5680E /* eventman.h */,
DF84245A0E7BA6AB00F5680E /* logic.cpp */,
@@ -6352,16 +6839,6 @@
DF8424AA0E7BA6AB00F5680E /* tinsel */ = {
isa = PBXGroup;
children = (
- DF2FFC1F0F4862520006E566 /* bmv.cpp */,
- DF2FFC200F4862520006E566 /* dialogs.cpp */,
- DF2FFC210F4862520006E566 /* dialogs.h */,
- DF2FFC220F4862520006E566 /* drives.cpp */,
- DF2FFC230F4862520006E566 /* drives.h */,
- DF2FFC240F4862520006E566 /* mareels.h */,
- DF2FFC250F4862520006E566 /* pdisplay.h */,
- DF2FFC260F4862520006E566 /* play.h */,
- DF2FFC270F4862520006E566 /* sysvar.cpp */,
- DF2FFC280F4862520006E566 /* sysvar.h */,
DF8424AB0E7BA6AB00F5680E /* actors.cpp */,
DF8424AC0E7BA6AB00F5680E /* actors.h */,
DF8424AD0E7BA6AB00F5680E /* anim.cpp */,
@@ -6369,6 +6846,7 @@
DF8424AF0E7BA6AB00F5680E /* background.cpp */,
DF8424B00E7BA6AB00F5680E /* background.h */,
DF8424B10E7BA6AB00F5680E /* bg.cpp */,
+ DF2FFC1F0F4862520006E566 /* bmv.cpp */,
DF8424B20E7BA6AB00F5680E /* cliprect.cpp */,
DF8424B30E7BA6AB00F5680E /* cliprect.h */,
DF8424B40E7BA6AB00F5680E /* config.cpp */,
@@ -6379,6 +6857,10 @@
DF8424B90E7BA6AB00F5680E /* debugger.cpp */,
DF8424BA0E7BA6AB00F5680E /* debugger.h */,
DF8424BB0E7BA6AB00F5680E /* detection.cpp */,
+ DF2FFC200F4862520006E566 /* dialogs.cpp */,
+ DF2FFC210F4862520006E566 /* dialogs.h */,
+ DF2FFC220F4862520006E566 /* drives.cpp */,
+ DF2FFC230F4862520006E566 /* drives.h */,
DF8424BC0E7BA6AB00F5680E /* dw.h */,
DF8424BD0E7BA6AB00F5680E /* effect.cpp */,
DF8424BE0E7BA6AB00F5680E /* events.cpp */,
@@ -6395,6 +6877,7 @@
DF8424C90E7BA6AB00F5680E /* heapmem.cpp */,
DF8424CA0E7BA6AB00F5680E /* heapmem.h */,
DF8424CD0E7BA6AB00F5680E /* mareels.cpp */,
+ DF2FFC240F4862520006E566 /* mareels.h */,
DF8424CF0E7BA6AB00F5680E /* move.cpp */,
DF8424D00E7BA6AB00F5680E /* move.h */,
DF8424D10E7BA6AB00F5680E /* multiobj.cpp */,
@@ -6408,8 +6891,10 @@
DF8424D90E7BA6AB00F5680E /* pcode.cpp */,
DF8424DA0E7BA6AB00F5680E /* pcode.h */,
DF8424DB0E7BA6AB00F5680E /* pdisplay.cpp */,
+ DF2FFC250F4862520006E566 /* pdisplay.h */,
DF8424DC0E7BA6AB00F5680E /* pid.h */,
DF8424DD0E7BA6AB00F5680E /* play.cpp */,
+ DF2FFC260F4862520006E566 /* play.h */,
DF8424DE0E7BA6AB00F5680E /* polygons.cpp */,
DF8424DF0E7BA6AB00F5680E /* polygons.h */,
DF8424E00E7BA6AB00F5680E /* rince.cpp */,
@@ -6429,6 +6914,8 @@
DF8424EF0E7BA6AB00F5680E /* sound.h */,
DF8424F00E7BA6AB00F5680E /* strres.cpp */,
DF8424F10E7BA6AB00F5680E /* strres.h */,
+ DF2FFC270F4862520006E566 /* sysvar.cpp */,
+ DF2FFC280F4862520006E566 /* sysvar.h */,
DF8424F20E7BA6AB00F5680E /* text.cpp */,
DF8424F30E7BA6AB00F5680E /* text.h */,
DF8424F40E7BA6AB00F5680E /* timers.cpp */,
@@ -6462,16 +6949,44 @@
path = touche;
sourceTree = "<group>";
};
+ DF895C0C124C24C00077F6E8 /* fmtowns_pc98 */ = {
+ isa = PBXGroup;
+ children = (
+ DF895C0D124C24C00077F6E8 /* towns_audio.cpp */,
+ DF895C0E124C24C00077F6E8 /* towns_audio.h */,
+ DF895C0F124C24C00077F6E8 /* towns_euphony.cpp */,
+ DF895C10124C24C00077F6E8 /* towns_euphony.h */,
+ DF895C11124C24C00077F6E8 /* towns_pc98_driver.cpp */,
+ DF895C12124C24C00077F6E8 /* towns_pc98_driver.h */,
+ DF895C13124C24C00077F6E8 /* towns_pc98_fmsynth.cpp */,
+ DF895C14124C24C00077F6E8 /* towns_pc98_fmsynth.h */,
+ );
+ path = fmtowns_pc98;
+ sourceTree = "<group>";
+ };
DF90EABF10B023F300C8F93F /* codecs */ = {
isa = PBXGroup;
children = (
DFB0579611B7549C0015AE65 /* cinepak.cpp */,
DFB0579711B7549C0015AE65 /* cinepak.h */,
+ DF90EAC010B023F400C8F93F /* codec.h */,
+ DF895CAB124E58980077F6E8 /* indeo3.cpp */,
+ DF895CAC124E58980077F6E8 /* indeo3.h */,
+ DF895CAD124E58980077F6E8 /* mjpeg.cpp */,
+ DF895CAE124E58980077F6E8 /* mjpeg.h */,
DFCDC6FC11662AD700A7D2A0 /* msrle.cpp */,
DFCDC6FD11662AD700A7D2A0 /* msrle.h */,
- DF90EAC010B023F400C8F93F /* codec.h */,
DF90EAC110B023F400C8F93F /* msvideo1.cpp */,
DF90EAC210B023F400C8F93F /* msvideo1.h */,
+ DF895CAF124E58980077F6E8 /* qdm2.cpp */,
+ DF895CB0124E58980077F6E8 /* qdm2.h */,
+ DF895CB1124E58980077F6E8 /* qdm2data.h */,
+ DF895CB2124E58980077F6E8 /* qtrle.cpp */,
+ DF895CB3124E58980077F6E8 /* qtrle.h */,
+ DF895CB4124E58980077F6E8 /* rpza.cpp */,
+ DF895CB5124E58980077F6E8 /* rpza.h */,
+ DF895CB6124E58980077F6E8 /* smc.cpp */,
+ DF895CB7124E58980077F6E8 /* smc.h */,
);
path = codecs;
sourceTree = "<group>";
@@ -6493,27 +7008,27 @@
DFC830190F48AF18005EF03C /* sci */ = {
isa = PBXGroup;
children = (
- DFB0577D11B7541F0015AE65 /* resource_audio.cpp */,
- DFB0577E11B7541F0015AE65 /* util.cpp */,
- DFB0577F11B7541F0015AE65 /* util.h */,
- DF45B175116628A5009B85CC /* graphics */,
- DF45B1A5116628A5009B85CC /* parser */,
- DF45B1AB116628A5009B85CC /* sound */,
- DF45B1C5116628A5009B85CC /* video */,
- DF2EC40310E64C8000765801 /* event.cpp */,
- DF2EC40410E64C8000765801 /* event.h */,
- DF90E9B410AEDA5300C8F93F /* detection_tables.h */,
+ DFAAD2390F50120E00C3A4E2 /* console.cpp */,
+ DFAAD23A0F50120E00C3A4E2 /* console.h */,
DF6118420FE3A8250042AD3F /* debug.h */,
DF6118430FE3A8250042AD3F /* decompressor.cpp */,
DF6118440FE3A8250042AD3F /* decompressor.h */,
- DF6118450FE3A8250042AD3F /* resource.cpp */,
- DF6118460FE3A8250042AD3F /* resource.h */,
- DFAAD2390F50120E00C3A4E2 /* console.cpp */,
- DFAAD23A0F50120E00C3A4E2 /* console.h */,
DFC8301A0F48AF18005EF03C /* detection.cpp */,
+ DF90E9B410AEDA5300C8F93F /* detection_tables.h */,
DFC8301B0F48AF18005EF03C /* engine */,
+ DF2EC40310E64C8000765801 /* event.cpp */,
+ DF2EC40410E64C8000765801 /* event.h */,
+ DF45B175116628A5009B85CC /* graphics */,
+ DF45B1A5116628A5009B85CC /* parser */,
+ DF6118450FE3A8250042AD3F /* resource.cpp */,
+ DF6118460FE3A8250042AD3F /* resource.h */,
+ DFB0577D11B7541F0015AE65 /* resource_audio.cpp */,
DFC830960F48AF18005EF03C /* sci.cpp */,
DFC830970F48AF18005EF03C /* sci.h */,
+ DF45B1AB116628A5009B85CC /* sound */,
+ DFB0577E11B7541F0015AE65 /* util.cpp */,
+ DFB0577F11B7541F0015AE65 /* util.h */,
+ DF45B1C5116628A5009B85CC /* video */,
);
path = sci;
sourceTree = "<group>";
@@ -6521,47 +7036,49 @@
DFC8301B0F48AF18005EF03C /* engine */ = {
isa = PBXGroup;
children = (
- DF7F286411FF23EF00159131 /* kvideo.cpp */,
- DF7F286511FF23EF00159131 /* workarounds.cpp */,
- DF7F286611FF23EF00159131 /* workarounds.h */,
DFCDC6D5116629CE00A7D2A0 /* features.cpp */,
DFCDC6D6116629CE00A7D2A0 /* features.h */,
- DFCDC6D7116629CE00A7D2A0 /* kparse.cpp */,
- DFCDC6D8116629CE00A7D2A0 /* selector.h */,
- DF90E9BD10AEDA9B00C8F93F /* selector.cpp */,
- DF7585F6100CB75800CC3324 /* static_selectors.cpp */,
- DF6118380FE3A8080042AD3F /* kmisc.cpp */,
- DF6118390FE3A8080042AD3F /* segment.cpp */,
- DF61183A0FE3A8080042AD3F /* segment.h */,
- DF61183B0FE3A8080042AD3F /* savegame.h */,
- DF89C2A30F62D79E00D756B6 /* script.cpp */,
- DF573BFE0F5A81EA00961A72 /* kernel.h */,
- DF573BFF0F5A81EA00961A72 /* script.h */,
- DF573C000F5A81EA00961A72 /* seg_manager.h */,
- DF573C010F5A81EA00961A72 /* state.cpp */,
- DF573C020F5A81EA00961A72 /* state.h */,
- DF573C030F5A81EA00961A72 /* vm.h */,
- DF573C040F5A81EA00961A72 /* vm_types.h */,
DFC8301E0F48AF18005EF03C /* gc.cpp */,
DFC8301F0F48AF18005EF03C /* gc.h */,
DFC830230F48AF18005EF03C /* kernel.cpp */,
+ DF573BFE0F5A81EA00961A72 /* kernel.h */,
+ DF895C28124C25350077F6E8 /* kernel_tables.h */,
DFC830260F48AF18005EF03C /* kevent.cpp */,
DFC830270F48AF18005EF03C /* kfile.cpp */,
DFC830280F48AF18005EF03C /* kgraphics.cpp */,
DFC830290F48AF18005EF03C /* klists.cpp */,
DFC8302A0F48AF18005EF03C /* kmath.cpp */,
DFC8302B0F48AF18005EF03C /* kmenu.cpp */,
+ DF6118380FE3A8080042AD3F /* kmisc.cpp */,
DFC8302C0F48AF18005EF03C /* kmovement.cpp */,
+ DFCDC6D7116629CE00A7D2A0 /* kparse.cpp */,
DFC8302D0F48AF18005EF03C /* kpathing.cpp */,
DFC8302E0F48AF18005EF03C /* kscripts.cpp */,
DFC8302F0F48AF18005EF03C /* ksound.cpp */,
DFC830300F48AF18005EF03C /* kstring.cpp */,
+ DF7F286411FF23EF00159131 /* kvideo.cpp */,
DFC830310F48AF18005EF03C /* message.cpp */,
DFC830320F48AF18005EF03C /* message.h */,
DFC830360F48AF18005EF03C /* savegame.cpp */,
+ DF61183B0FE3A8080042AD3F /* savegame.h */,
+ DF89C2A30F62D79E00D756B6 /* script.cpp */,
+ DF573BFF0F5A81EA00961A72 /* script.h */,
+ DF895C29124C25350077F6E8 /* script_patches.cpp */,
DFC830390F48AF18005EF03C /* scriptdebug.cpp */,
DFC8303A0F48AF18005EF03C /* seg_manager.cpp */,
+ DF573C000F5A81EA00961A72 /* seg_manager.h */,
+ DF6118390FE3A8080042AD3F /* segment.cpp */,
+ DF61183A0FE3A8080042AD3F /* segment.h */,
+ DF90E9BD10AEDA9B00C8F93F /* selector.cpp */,
+ DFCDC6D8116629CE00A7D2A0 /* selector.h */,
+ DF573C010F5A81EA00961A72 /* state.cpp */,
+ DF573C020F5A81EA00961A72 /* state.h */,
+ DF7585F6100CB75800CC3324 /* static_selectors.cpp */,
DFC8303C0F48AF18005EF03C /* vm.cpp */,
+ DF573C030F5A81EA00961A72 /* vm.h */,
+ DF573C040F5A81EA00961A72 /* vm_types.h */,
+ DF7F286511FF23EF00159131 /* workarounds.cpp */,
+ DF7F286611FF23EF00159131 /* workarounds.h */,
);
path = engine;
sourceTree = "<group>";
@@ -6569,18 +7086,18 @@
DFD5184C0DF3420D00854012 /* scaler */ = {
isa = PBXGroup;
children = (
- DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */,
DFD518AA0DF34BA600854012 /* 2xsai.cpp */,
DFD518AB0DF34BA600854012 /* aspect.cpp */,
DFD518AD0DF34BA600854012 /* hq2x.cpp */,
DFD518B10DF34BA600854012 /* hq3x.cpp */,
+ DFD5189E0DF34AD700854012 /* intern.h */,
DFD518B50DF34BA600854012 /* scale2x.cpp */,
DFD518B60DF34BA600854012 /* scale2x.h */,
DFD518B80DF34BA600854012 /* scale3x.cpp */,
DFD518B90DF34BA600854012 /* scale3x.h */,
DFD518A00DF34B2500854012 /* scalebit.cpp */,
DFD518A10DF34B2500854012 /* scalebit.h */,
- DFD5189E0DF34AD700854012 /* intern.h */,
+ DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */,
);
path = scaler;
sourceTree = "<group>";
@@ -6765,46 +7282,23 @@
DFE473950D81F4E800B6D1FB /* common */ = {
isa = PBXGroup;
children = (
- DF7F289111FF247300159131 /* translation.cpp */,
- DFB0577311B753DA0015AE65 /* debug-channels.h */,
- DFB0577411B753DA0015AE65 /* rational.cpp */,
- DFB0577511B753DA0015AE65 /* rational.h */,
- DF9B9261118E46FE0069C19D /* error.cpp */,
- DFEC5D0A1166C5CF00C90552 /* random.cpp */,
- DFEC5D0B1166C5CF00C90552 /* random.h */,
- DFEC5D0C1166C5CF00C90552 /* str-array.h */,
- DFEC5D0D1166C5CF00C90552 /* tokenizer.cpp */,
- DFEC5D0E1166C5CF00C90552 /* tokenizer.h */,
- DFEC5D0F1166C5CF00C90552 /* types.h */,
- DFCDC70911662B6B00A7D2A0 /* macresman.cpp */,
- DFCDC70A11662B6B00A7D2A0 /* macresman.h */,
- DF2EC50910E64DB300765801 /* textconsole.cpp */,
- DF2EC50A10E64DB300765801 /* textconsole.h */,
- DF6BF4F710529F140069811F /* EventDispatcher.cpp */,
- DF6BF4F810529F140069811F /* EventRecorder.cpp */,
- DF6BF4F910529F140069811F /* EventRecorder.h */,
- DF6BF4FA10529F140069811F /* list_intern.h */,
- DF6BF4FB10529F140069811F /* serializer.h */,
- DF2FFBD10F485DFB0006E566 /* debug.cpp */,
- DF2FFBD20F485DFB0006E566 /* debug.h */,
- DF7E8C0F0ED5FCC2001CB19F /* xmlparser.cpp */,
- DF7E8C100ED5FCC2001CB19F /* xmlparser.h */,
+ DFE473980D81F4E800B6D1FB /* algorithm.h */,
DF842A400E7BBBB400F5680E /* archive.cpp */,
DF842A410E7BBBB400F5680E /* archive.h */,
- DF842A430E7BBBB400F5680E /* ptr.h */,
- DF842A440E7BBBB400F5680E /* queue.h */,
- DF842A450E7BBBB400F5680E /* unarj.cpp */,
- DF842A460E7BBBB400F5680E /* unarj.h */,
- DFD511460DF3383500854012 /* memorypool.cpp */,
- DFD511470DF3383500854012 /* memorypool.h */,
- DFE473980D81F4E800B6D1FB /* algorithm.h */,
DFE473990D81F4E800B6D1FB /* array.h */,
DFE4739A0D81F4E800B6D1FB /* config-file.cpp */,
DFE4739B0D81F4E800B6D1FB /* config-file.h */,
DFE4739C0D81F4E800B6D1FB /* config-manager.cpp */,
DFE4739D0D81F4E800B6D1FB /* config-manager.h */,
+ DFB0577311B753DA0015AE65 /* debug-channels.h */,
+ DF2FFBD10F485DFB0006E566 /* debug.cpp */,
+ DF2FFBD20F485DFB0006E566 /* debug.h */,
DFE4739E0D81F4E800B6D1FB /* endian.h */,
+ DF9B9261118E46FE0069C19D /* error.cpp */,
DFE4739F0D81F4E800B6D1FB /* error.h */,
+ DF6BF4F710529F140069811F /* EventDispatcher.cpp */,
+ DF6BF4F810529F140069811F /* EventRecorder.cpp */,
+ DF6BF4F910529F140069811F /* EventRecorder.h */,
DFE473A00D81F4E800B6D1FB /* events.h */,
DFE473A10D81F4E800B6D1FB /* file.cpp */,
DFE473A20D81F4E800B6D1FB /* file.h */,
@@ -6818,29 +7312,52 @@
DFE473AA0D81F4E800B6D1FB /* iff_container.h */,
DFE473AB0D81F4E800B6D1FB /* keyboard.h */,
DFE473AC0D81F4E800B6D1FB /* list.h */,
+ DF6BF4FA10529F140069811F /* list_intern.h */,
+ DFCDC70911662B6B00A7D2A0 /* macresman.cpp */,
+ DFCDC70A11662B6B00A7D2A0 /* macresman.h */,
DFE473AD0D81F4E800B6D1FB /* md5.cpp */,
DFE473AE0D81F4E800B6D1FB /* md5.h */,
+ DFD511460DF3383500854012 /* memorypool.cpp */,
+ DFD511470DF3383500854012 /* memorypool.h */,
DFE473B00D81F4E800B6D1FB /* mutex.cpp */,
DFE473B10D81F4E800B6D1FB /* mutex.h */,
DFE473B20D81F4E800B6D1FB /* noncopyable.h */,
DFE473B30D81F4E800B6D1FB /* pack-end.h */,
DFE473B40D81F4E800B6D1FB /* pack-start.h */,
+ DF842A430E7BBBB400F5680E /* ptr.h */,
+ DF842A440E7BBBB400F5680E /* queue.h */,
+ DFEC5D0A1166C5CF00C90552 /* random.cpp */,
+ DFEC5D0B1166C5CF00C90552 /* random.h */,
+ DFB0577411B753DA0015AE65 /* rational.cpp */,
+ DFB0577511B753DA0015AE65 /* rational.h */,
DFE473B50D81F4E800B6D1FB /* rect.h */,
DFE473B60D81F4E800B6D1FB /* savefile.h */,
DFE473B70D81F4E800B6D1FB /* scummsys.h */,
+ DF6BF4FB10529F140069811F /* serializer.h */,
DFE473B80D81F4E800B6D1FB /* singleton.h */,
DFE473B90D81F4E800B6D1FB /* stack.h */,
+ DFEC5D0C1166C5CF00C90552 /* str-array.h */,
DFE473BA0D81F4E800B6D1FB /* str.cpp */,
DFE473BB0D81F4E800B6D1FB /* str.h */,
DFE473BC0D81F4E800B6D1FB /* stream.cpp */,
DFE473BD0D81F4E800B6D1FB /* stream.h */,
DFE473BE0D81F4E800B6D1FB /* system.cpp */,
DFE473BF0D81F4E800B6D1FB /* system.h */,
+ DF2EC50910E64DB300765801 /* textconsole.cpp */,
+ DF2EC50A10E64DB300765801 /* textconsole.h */,
DFE473C00D81F4E800B6D1FB /* timer.h */,
+ DFEC5D0D1166C5CF00C90552 /* tokenizer.cpp */,
+ DFEC5D0E1166C5CF00C90552 /* tokenizer.h */,
+ DF7F289111FF247300159131 /* translation.cpp */,
+ DFEC5D0F1166C5CF00C90552 /* types.h */,
+ DF842A450E7BBBB400F5680E /* unarj.cpp */,
+ DF842A460E7BBBB400F5680E /* unarj.h */,
DFE473C10D81F4E800B6D1FB /* unzip.cpp */,
DFE473C20D81F4E800B6D1FB /* unzip.h */,
DFE473C30D81F4E800B6D1FB /* util.cpp */,
DFE473C40D81F4E800B6D1FB /* util.h */,
+ DF7E8C0F0ED5FCC2001CB19F /* xmlparser.cpp */,
+ DF7E8C100ED5FCC2001CB19F /* xmlparser.h */,
DFE473C50D81F4E800B6D1FB /* zlib.cpp */,
DFE473C60D81F4E800B6D1FB /* zlib.h */,
);
@@ -6851,30 +7368,13 @@
DFE477520D81F4E900B6D1FB /* graphics */ = {
isa = PBXGroup;
children = (
- DFB0578F11B7547D0015AE65 /* pict.cpp */,
- DFB0579011B7547D0015AE65 /* pict.h */,
+ DFE477530D81F4E900B6D1FB /* colormasks.h */,
DF6BF4C010529DA50069811F /* conversion.cpp */,
DF6BF4C110529DA50069811F /* conversion.h */,
- DF6BF4C210529DA50069811F /* jpeg.cpp */,
- DF6BF4C310529DA50069811F /* jpeg.h */,
- DF7585EA100CB6EA00CC3324 /* sjis.cpp */,
- DF7585EB100CB6EA00CC3324 /* sjis.h */,
- DF2FFB940F485D950006E566 /* video */,
- DF2FFB900F485D890006E566 /* dither.cpp */,
- DF2FFB910F485D890006E566 /* dither.h */,
- DF2FFB920F485D890006E566 /* pixelformat.h */,
- DF7E8C050ED5FCAF001CB19F /* thumbnail.cpp */,
- DF7E8C060ED5FCAF001CB19F /* thumbnail.h */,
- DF7E8C070ED5FCAF001CB19F /* VectorRenderer.cpp */,
- DF7E8C080ED5FCAF001CB19F /* VectorRenderer.h */,
- DF7E8C090ED5FCAF001CB19F /* VectorRendererSpec.cpp */,
- DF7E8C0A0ED5FCAF001CB19F /* VectorRendererSpec.h */,
- DFD5184C0DF3420D00854012 /* scaler */,
- DFD5183B0DF3411800854012 /* scaler.cpp */,
- DFD5183C0DF3411800854012 /* scaler.h */,
- DFE477530D81F4E900B6D1FB /* colormasks.h */,
DFE477540D81F4E900B6D1FB /* cursorman.cpp */,
DFE477550D81F4E900B6D1FB /* cursorman.h */,
+ DF2FFB900F485D890006E566 /* dither.cpp */,
+ DF2FFB910F485D890006E566 /* dither.h */,
DFE477580D81F4E900B6D1FB /* font.cpp */,
DFE477590D81F4E900B6D1FB /* font.h */,
DFE4775A0D81F4E900B6D1FB /* fontman.cpp */,
@@ -6884,10 +7384,27 @@
DFE477620D81F4E900B6D1FB /* iff.h */,
DFE477630D81F4E900B6D1FB /* imagedec.cpp */,
DFE477640D81F4E900B6D1FB /* imagedec.h */,
+ DF6BF4C210529DA50069811F /* jpeg.cpp */,
+ DF6BF4C310529DA50069811F /* jpeg.h */,
+ DFB0578F11B7547D0015AE65 /* pict.cpp */,
+ DFB0579011B7547D0015AE65 /* pict.h */,
+ DF2FFB920F485D890006E566 /* pixelformat.h */,
DFE4776A0D81F4E900B6D1FB /* primitives.cpp */,
DFE4776B0D81F4E900B6D1FB /* primitives.h */,
+ DFD5184C0DF3420D00854012 /* scaler */,
+ DFD5183B0DF3411800854012 /* scaler.cpp */,
+ DFD5183C0DF3411800854012 /* scaler.h */,
+ DF7585EA100CB6EA00CC3324 /* sjis.cpp */,
+ DF7585EB100CB6EA00CC3324 /* sjis.h */,
DFE477860D81F4E900B6D1FB /* surface.cpp */,
DFE477870D81F4E900B6D1FB /* surface.h */,
+ DF7E8C050ED5FCAF001CB19F /* thumbnail.cpp */,
+ DF7E8C060ED5FCAF001CB19F /* thumbnail.h */,
+ DF7E8C070ED5FCAF001CB19F /* VectorRenderer.cpp */,
+ DF7E8C080ED5FCAF001CB19F /* VectorRenderer.h */,
+ DF7E8C090ED5FCAF001CB19F /* VectorRendererSpec.cpp */,
+ DF7E8C0A0ED5FCAF001CB19F /* VectorRendererSpec.h */,
+ DF2FFB940F485D950006E566 /* video */,
);
name = graphics;
path = ../../graphics;
@@ -6907,29 +7424,13 @@
DFE477880D81F4E900B6D1FB /* gui */ = {
isa = PBXGroup;
children = (
- DF7F288911FF244F00159131 /* Tooltip.cpp */,
- DF7F288A11FF244F00159131 /* Tooltip.h */,
- DF9B9246118E46730069C19D /* error.cpp */,
- DF9B9247118E46730069C19D /* error.h */,
- DF2EC3E410E6490800765801 /* browser_osx.mm */,
- DF2FFBD50F485E360006E566 /* GuiManager.cpp */,
- DF2FFBD60F485E360006E566 /* GuiManager.h */,
- DF7E8BF00ED5FC77001CB19F /* saveload.cpp */,
- DF7E8BF10ED5FC77001CB19F /* saveload.h */,
- DF7E8BF40ED5FC77001CB19F /* ThemeEngine.cpp */,
- DF7E8BF50ED5FC77001CB19F /* ThemeEngine.h */,
- DF7E8BF60ED5FC77001CB19F /* ThemeEval.cpp */,
- DF7E8BF70ED5FC77001CB19F /* ThemeEval.h */,
- DF7E8BF80ED5FC77001CB19F /* ThemeLayout.cpp */,
- DF7E8BF90ED5FC77001CB19F /* ThemeLayout.h */,
- DF7E8BFA0ED5FC77001CB19F /* ThemeParser.cpp */,
- DF7E8BFB0ED5FC77001CB19F /* ThemeParser.h */,
DFE477890D81F4E900B6D1FB /* about.cpp */,
DFE4778A0D81F4E900B6D1FB /* about.h */,
DFE4778B0D81F4E900B6D1FB /* Actions.cpp */,
DFE4778C0D81F4E900B6D1FB /* Actions.h */,
DFE4778D0D81F4E900B6D1FB /* browser.cpp */,
DFE4778E0D81F4E900B6D1FB /* browser.h */,
+ DF2EC3E410E6490800765801 /* browser_osx.mm */,
DFE4778F0D81F4E900B6D1FB /* chooser.cpp */,
DFE477900D81F4E900B6D1FB /* chooser.h */,
DFE477910D81F4E900B6D1FB /* console.cpp */,
@@ -6943,6 +7444,10 @@
DFE477990D81F4E900B6D1FB /* editable.h */,
DFE4779A0D81F4E900B6D1FB /* EditTextWidget.cpp */,
DFE4779B0D81F4E900B6D1FB /* EditTextWidget.h */,
+ DF9B9246118E46730069C19D /* error.cpp */,
+ DF9B9247118E46730069C19D /* error.h */,
+ DF2FFBD50F485E360006E566 /* GuiManager.cpp */,
+ DF2FFBD60F485E360006E566 /* GuiManager.h */,
DFE4779E0D81F4E900B6D1FB /* Key.cpp */,
DFE4779F0D81F4E900B6D1FB /* Key.h */,
DFE477A20D81F4E900B6D1FB /* launcher.cpp */,
@@ -6959,13 +7464,25 @@
DFE477B00D81F4E900B6D1FB /* options.h */,
DFE477B10D81F4E900B6D1FB /* PopUpWidget.cpp */,
DFE477B20D81F4E900B6D1FB /* PopUpWidget.h */,
+ DF7E8BF00ED5FC77001CB19F /* saveload.cpp */,
+ DF7E8BF10ED5FC77001CB19F /* saveload.h */,
DFE477B30D81F4E900B6D1FB /* ScrollBarWidget.cpp */,
DFE477B40D81F4E900B6D1FB /* ScrollBarWidget.h */,
DFE477B50D81F4E900B6D1FB /* TabWidget.cpp */,
DFE477B60D81F4E900B6D1FB /* TabWidget.h */,
DFE477BA0D81F4E900B6D1FB /* themebrowser.cpp */,
DFE477BB0D81F4E900B6D1FB /* themebrowser.h */,
+ DF7E8BF40ED5FC77001CB19F /* ThemeEngine.cpp */,
+ DF7E8BF50ED5FC77001CB19F /* ThemeEngine.h */,
+ DF7E8BF60ED5FC77001CB19F /* ThemeEval.cpp */,
+ DF7E8BF70ED5FC77001CB19F /* ThemeEval.h */,
+ DF7E8BF80ED5FC77001CB19F /* ThemeLayout.cpp */,
+ DF7E8BF90ED5FC77001CB19F /* ThemeLayout.h */,
+ DF7E8BFA0ED5FC77001CB19F /* ThemeParser.cpp */,
+ DF7E8BFB0ED5FC77001CB19F /* ThemeParser.h */,
DFE477C00D81F4E900B6D1FB /* themes */,
+ DF7F288911FF244F00159131 /* Tooltip.cpp */,
+ DF7F288A11FF244F00159131 /* Tooltip.h */,
DFE477C40D81F4E900B6D1FB /* widget.cpp */,
DFE477C50D81F4E900B6D1FB /* widget.h */,
);
@@ -6985,15 +7502,11 @@
DFE477C60D81F4E900B6D1FB /* sound */ = {
isa = PBXGroup;
children = (
- DF45B0EB116627D9009B85CC /* decoders */,
- DF89C2B80F62D91000D756B6 /* timestamp.cpp */,
- DF89C2B90F62D91000D756B6 /* timestamp.h */,
- DF842A6F0E7BBDB200F5680E /* musicplugin.cpp */,
- DF842A700E7BBDB200F5680E /* musicplugin.h */,
DFE477CB0D81F4E900B6D1FB /* audiocd.cpp */,
DFE477CC0D81F4E900B6D1FB /* audiocd.h */,
DFE477CD0D81F4E900B6D1FB /* audiostream.cpp */,
DFE477CE0D81F4E900B6D1FB /* audiostream.h */,
+ DF45B0EB116627D9009B85CC /* decoders */,
DFE477D10D81F4E900B6D1FB /* fmopl.cpp */,
DFE477D20D81F4E900B6D1FB /* fmopl.h */,
DFE477D50D81F4E900B6D1FB /* mididrv.cpp */,
@@ -7007,10 +7520,14 @@
DFE477DD0D81F4E900B6D1FB /* mods */,
DFE477ED0D81F4E900B6D1FB /* mpu401.cpp */,
DFE477EE0D81F4E900B6D1FB /* mpu401.h */,
+ DF842A6F0E7BBDB200F5680E /* musicplugin.cpp */,
+ DF842A700E7BBDB200F5680E /* musicplugin.h */,
DFE477EF0D81F4E900B6D1FB /* null.cpp */,
DFE477F00D81F4E900B6D1FB /* rate.cpp */,
DFE477F10D81F4E900B6D1FB /* rate.h */,
DFE477F60D81F4E900B6D1FB /* softsynth */,
+ DF89C2B80F62D91000D756B6 /* timestamp.cpp */,
+ DF89C2B90F62D91000D756B6 /* timestamp.h */,
);
name = sound;
path = ../../sound;
@@ -7019,12 +7536,10 @@
DFE477DD0D81F4E900B6D1FB /* mods */ = {
isa = PBXGroup;
children = (
- DF6BF50210529F540069811F /* maxtrax.cpp */,
- DF6BF50310529F540069811F /* maxtrax.h */,
- DF6BF50410529F540069811F /* tfmx.cpp */,
- DF6BF50510529F540069811F /* tfmx.h */,
DFE477DE0D81F4E900B6D1FB /* infogrames.cpp */,
DFE477DF0D81F4E900B6D1FB /* infogrames.h */,
+ DF6BF50210529F540069811F /* maxtrax.cpp */,
+ DF6BF50310529F540069811F /* maxtrax.h */,
DFE477E00D81F4E900B6D1FB /* module.cpp */,
DFE477E10D81F4E900B6D1FB /* module.h */,
DFE477E20D81F4E900B6D1FB /* paula.cpp */,
@@ -7035,6 +7550,8 @@
DFE477E70D81F4E900B6D1FB /* rjp1.h */,
DFE477E80D81F4E900B6D1FB /* soundfx.cpp */,
DFE477E90D81F4E900B6D1FB /* soundfx.h */,
+ DF6BF50410529F540069811F /* tfmx.cpp */,
+ DF6BF50510529F540069811F /* tfmx.h */,
);
path = mods;
sourceTree = "<group>";
@@ -7042,15 +7559,19 @@
DFE477F60D81F4E900B6D1FB /* softsynth */ = {
isa = PBXGroup;
children = (
- DF2EC51710E64EE600765801 /* wave6581.cpp */,
- DF2EC51010E64E3100765801 /* sid.cpp */,
- DF2EC51110E64E3100765801 /* sid.h */,
- DFF958A80FB222F300A3EC78 /* opl */,
DFE477F70D81F4E900B6D1FB /* adlib.cpp */,
+ DF895C08124C24B50077F6E8 /* appleiigs.cpp */,
+ DF0E303F1252C6090082D593 /* cms.cpp */,
+ DF0E30401252C6090082D593 /* cms.h */,
DFE477F80D81F4E900B6D1FB /* emumidi.h */,
DFE477F90D81F4E900B6D1FB /* fluidsynth.cpp */,
+ DF895C0C124C24C00077F6E8 /* fmtowns_pc98 */,
+ DFF958A80FB222F300A3EC78 /* opl */,
DFE478210D81F4E900B6D1FB /* pcspk.cpp */,
DFE478220D81F4E900B6D1FB /* pcspk.h */,
+ DF2EC51010E64E3100765801 /* sid.cpp */,
+ DF2EC51110E64E3100765801 /* sid.h */,
+ DF2EC51710E64EE600765801 /* wave6581.cpp */,
DFE478230D81F4E900B6D1FB /* ym2612.cpp */,
DFE478240D81F4E900B6D1FB /* ym2612.h */,
);
@@ -7062,10 +7583,10 @@
children = (
DFEC5D3D1166C6B400C90552 /* dbopl.cpp */,
DFEC5D3E1166C6B400C90552 /* dbopl.h */,
- DF6118CF0FE3AB560042AD3F /* mame.cpp */,
- DF6118D00FE3AB560042AD3F /* mame.h */,
DFF958A90FB222F300A3EC78 /* dosbox.cpp */,
DFF958AA0FB222F300A3EC78 /* dosbox.h */,
+ DF6118CF0FE3AB560042AD3F /* mame.cpp */,
+ DF6118D00FE3AB560042AD3F /* mame.h */,
);
path = opl;
sourceTree = "<group>";
@@ -7134,7 +7655,14 @@
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "scummvm" */;
compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
projectDirPath = "";
projectRoot = "";
@@ -7159,6 +7687,13 @@
DFE47C8B0D81F86900B6D1FB /* sky.cpt in Resources */,
DF2FFD2B0F48717F0006E566 /* Default.png in Resources */,
DF2FFD2C0F48717F0006E566 /* icon.png in Resources */,
+ DF895C34124C26660077F6E8 /* icon-72.png in Resources */,
+ DF895C41124C271F0077F6E8 /* icon4.png in Resources */,
+ 8CB5A9E11253FDF500CB6BC7 /* drascula.dat in Resources */,
+ 8CB5A9E21253FDF500CB6BC7 /* hugo.dat in Resources */,
+ 8CB5A9E31253FDF500CB6BC7 /* m4.dat in Resources */,
+ 8CB5A9E41253FDF500CB6BC7 /* teenagent.dat in Resources */,
+ 8CD1ED8A1262030100FA198C /* toon.dat in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7173,6 +7708,13 @@
DF093E7C0F63CB26002D821E /* queen.tbl in Resources */,
DF093E7D0F63CB26002D821E /* sky.cpt in Resources */,
DF0944B10F6430ED002D821E /* scummvm.icns in Resources */,
+ DF895C35124C26660077F6E8 /* icon-72.png in Resources */,
+ DF895C42124C271F0077F6E8 /* icon4.png in Resources */,
+ 8CB5A9DD1253FDF500CB6BC7 /* drascula.dat in Resources */,
+ 8CB5A9DE1253FDF500CB6BC7 /* hugo.dat in Resources */,
+ 8CB5A9DF1253FDF500CB6BC7 /* m4.dat in Resources */,
+ 8CB5A9E01253FDF500CB6BC7 /* teenagent.dat in Resources */,
+ 8CD1ED891262030100FA198C /* toon.dat in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7188,6 +7730,13 @@
DFF959130FB22D5700A3EC78 /* sky.cpt in Resources */,
DFF959140FB22D5700A3EC78 /* Default.png in Resources */,
DFF959150FB22D5700A3EC78 /* icon.png in Resources */,
+ DF895C36124C26660077F6E8 /* icon-72.png in Resources */,
+ DF895C43124C271F0077F6E8 /* icon4.png in Resources */,
+ 8CB5A9D91253FDF500CB6BC7 /* drascula.dat in Resources */,
+ 8CB5A9DA1253FDF500CB6BC7 /* hugo.dat in Resources */,
+ 8CB5A9DB1253FDF500CB6BC7 /* m4.dat in Resources */,
+ 8CB5A9DC1253FDF500CB6BC7 /* teenagent.dat in Resources */,
+ 8CD1ED881262030100FA198C /* toon.dat in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -7432,7 +7981,6 @@
DF8425E00E7BA6AC00F5680E /* draw_bargon.cpp in Sources */,
DF8425E10E7BA6AC00F5680E /* draw_v1.cpp in Sources */,
DF8425E20E7BA6AC00F5680E /* draw_v2.cpp in Sources */,
- DF8425E30E7BA6AC00F5680E /* driver_vga.cpp in Sources */,
DF8425E40E7BA6AC00F5680E /* game.cpp in Sources */,
DF8425E70E7BA6AC00F5680E /* global.cpp in Sources */,
DF8425E80E7BA6AC00F5680E /* gob.cpp in Sources */,
@@ -7744,7 +8292,6 @@
DF8427AD0E7BA6AC00F5680E /* insane_enemy.cpp in Sources */,
DF8427AE0E7BA6AC00F5680E /* insane_iact.cpp in Sources */,
DF8427AF0E7BA6AC00F5680E /* insane_scenes.cpp in Sources */,
- DF8427B00E7BA6AC00F5680E /* midiparser_eup.cpp in Sources */,
DF8427B10E7BA6AC00F5680E /* midiparser_ro.cpp in Sources */,
DF8427B30E7BA6AC00F5680E /* nut_renderer.cpp in Sources */,
DF8427B40E7BA6AC00F5680E /* object.cpp in Sources */,
@@ -8016,8 +8563,6 @@
DF61184C0FE3A8250042AD3F /* decompressor.cpp in Sources */,
DF61184D0FE3A8250042AD3F /* resource.cpp in Sources */,
DF6118560FE3A8990042AD3F /* disk.cpp in Sources */,
- DF61186B0FE3A9410042AD3F /* coktelvideo.cpp in Sources */,
- DF61186C0FE3A9410042AD3F /* indeo3.cpp in Sources */,
DF61186D0FE3A9410042AD3F /* dxa_decoder.cpp in Sources */,
DF61186E0FE3A9410042AD3F /* flic_decoder.cpp in Sources */,
DF61186F0FE3A9410042AD3F /* smk_decoder.cpp in Sources */,
@@ -8099,12 +8644,10 @@
DF45B1D4116628A5009B85CC /* menu.cpp in Sources */,
DF45B1D5116628A5009B85CC /* paint.cpp in Sources */,
DF45B1D6116628A5009B85CC /* paint16.cpp in Sources */,
- DF45B1D7116628A5009B85CC /* paint32.cpp in Sources */,
DF45B1D8116628A5009B85CC /* palette.cpp in Sources */,
DF45B1D9116628A5009B85CC /* picture.cpp in Sources */,
DF45B1DA116628A5009B85CC /* portrait.cpp in Sources */,
DF45B1DB116628A5009B85CC /* ports.cpp in Sources */,
- DF45B1DC116628A5009B85CC /* robot.cpp in Sources */,
DF45B1DD116628A5009B85CC /* screen.cpp in Sources */,
DF45B1DE116628A5009B85CC /* text16.cpp in Sources */,
DF45B1DF116628A5009B85CC /* transitions.cpp in Sources */,
@@ -8121,7 +8664,6 @@
DF45B1F0116628A5009B85CC /* music.cpp in Sources */,
DF45B1F1116628A5009B85CC /* soundcmd.cpp in Sources */,
DF45B1F2116628A5009B85CC /* seq_decoder.cpp in Sources */,
- DF45B1F3116628A5009B85CC /* vmd_decoder.cpp in Sources */,
DFCDC6D9116629CE00A7D2A0 /* features.cpp in Sources */,
DFCDC6DA116629CE00A7D2A0 /* kparse.cpp in Sources */,
DFCDC6F711662AAB00A7D2A0 /* resource.cpp in Sources */,
@@ -8144,7 +8686,6 @@
DFB0578B11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579211B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579911B7549C0015AE65 /* cinepak.cpp in Sources */,
- DF7F285E11FF23B700159131 /* frameout.cpp in Sources */,
DF7F286211FF23D500159131 /* amigamac.cpp in Sources */,
DF7F286911FF23EF00159131 /* kvideo.cpp in Sources */,
DF7F286A11FF23EF00159131 /* workarounds.cpp in Sources */,
@@ -8157,6 +8698,79 @@
DF7F289511FF247300159131 /* translation.cpp in Sources */,
DF7F28A111FF24B000159131 /* mac_snd.cpp in Sources */,
DF7F28A611FF24C400159131 /* console.cpp in Sources */,
+ DF895BFE124C24350077F6E8 /* coktel_decoder.cpp in Sources */,
+ DF895C03124C24680077F6E8 /* player_towns.cpp in Sources */,
+ DF895C09124C24B60077F6E8 /* appleiigs.cpp in Sources */,
+ DF895C15124C24C10077F6E8 /* towns_audio.cpp in Sources */,
+ DF895C16124C24C10077F6E8 /* towns_euphony.cpp in Sources */,
+ DF895C17124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */,
+ DF895C18124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */,
+ DF895C25124C25150077F6E8 /* init_fascin.cpp in Sources */,
+ DF895C2A124C25350077F6E8 /* script_patches.cpp in Sources */,
+ DF895CB8124E58980077F6E8 /* indeo3.cpp in Sources */,
+ DF895CB9124E58980077F6E8 /* mjpeg.cpp in Sources */,
+ DF895CBA124E58980077F6E8 /* qdm2.cpp in Sources */,
+ DF895CBB124E58980077F6E8 /* qtrle.cpp in Sources */,
+ DF895CBC124E58980077F6E8 /* rpza.cpp in Sources */,
+ DF895CBD124E58990077F6E8 /* smc.cpp in Sources */,
+ DF0E303B1252C5BD0082D593 /* cms.cpp in Sources */,
+ DF0E30421252C6090082D593 /* cms.cpp in Sources */,
+ 8CB5A9CB1253FD6900CB6BC7 /* m4_scene.cpp in Sources */,
+ 8CB5A9CC1253FD6900CB6BC7 /* mads_logic.cpp in Sources */,
+ 8CB5A9CD1253FD6900CB6BC7 /* mads_player.cpp in Sources */,
+ 8CB5A9CE1253FD6900CB6BC7 /* mads_scene.cpp in Sources */,
+ 8CB5A9CF1253FD6900CB6BC7 /* mads_views.cpp in Sources */,
+ 8CD1ED53126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED54126202AB00FA198C /* display.cpp in Sources */,
+ 8CD1ED55126202AB00FA198C /* engine.cpp in Sources */,
+ 8CD1ED56126202AB00FA198C /* file.cpp in Sources */,
+ 8CD1ED57126202AB00FA198C /* hugo.cpp in Sources */,
+ 8CD1ED58126202AB00FA198C /* intro.cpp in Sources */,
+ 8CD1ED59126202AB00FA198C /* inventory.cpp in Sources */,
+ 8CD1ED5C126202AB00FA198C /* mouse.cpp in Sources */,
+ 8CD1ED5D126202AB00FA198C /* parser.cpp in Sources */,
+ 8CD1ED5E126202AB00FA198C /* route.cpp in Sources */,
+ 8CD1ED5F126202AB00FA198C /* schedule.cpp in Sources */,
+ 8CD1ED60126202AB00FA198C /* sound.cpp in Sources */,
+ 8CD1ED61126202AB00FA198C /* util.cpp in Sources */,
+ 8CD1ED62126202AB00FA198C /* anim.cpp in Sources */,
+ 8CD1ED63126202AB00FA198C /* audio.cpp in Sources */,
+ 8CD1ED64126202AB00FA198C /* character.cpp in Sources */,
+ 8CD1ED65126202AB00FA198C /* conversation.cpp in Sources */,
+ 8CD1ED66126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED67126202AB00FA198C /* drew.cpp in Sources */,
+ 8CD1ED68126202AB00FA198C /* flux.cpp in Sources */,
+ 8CD1ED69126202AB00FA198C /* font.cpp in Sources */,
+ 8CD1ED6A126202AB00FA198C /* hotspot.cpp in Sources */,
+ 8CD1ED6D126202AB00FA198C /* movie.cpp in Sources */,
+ 8CD1ED6E126202AB00FA198C /* path.cpp in Sources */,
+ 8CD1ED6F126202AB00FA198C /* picture.cpp in Sources */,
+ 8CD1ED70126202AB00FA198C /* resource.cpp in Sources */,
+ 8CD1ED71126202AB00FA198C /* script.cpp in Sources */,
+ 8CD1ED72126202AB00FA198C /* script_func.cpp in Sources */,
+ 8CD1ED73126202AB00FA198C /* state.cpp in Sources */,
+ 8CD1ED74126202AB00FA198C /* text.cpp in Sources */,
+ 8CD1ED75126202AB00FA198C /* tools.cpp in Sources */,
+ 8CD1ED76126202AB00FA198C /* toon.cpp in Sources */,
+ 8CD80C8D126271A9001C6C87 /* surface.cpp in Sources */,
+ 8CD80C93126271BD001C6C87 /* gfx_towns.cpp in Sources */,
+ 8CD80D04126272A0001C6C87 /* actor.cpp in Sources */,
+ 8CD80D05126272A0001C6C87 /* animation.cpp in Sources */,
+ 8CD80D06126272A0001C6C87 /* callbacks.cpp in Sources */,
+ 8CD80D07126272A0001C6C87 /* console.cpp in Sources */,
+ 8CD80D08126272A0001C6C87 /* detection.cpp in Sources */,
+ 8CD80D09126272A0001C6C87 /* dialog.cpp in Sources */,
+ 8CD80D0A126272A0001C6C87 /* font.cpp in Sources */,
+ 8CD80D0B126272A0001C6C87 /* inventory.cpp in Sources */,
+ 8CD80D0D126272A0001C6C87 /* music.cpp in Sources */,
+ 8CD80D0E126272A0001C6C87 /* objects.cpp in Sources */,
+ 8CD80D0F126272A0001C6C87 /* pack.cpp in Sources */,
+ 8CD80D10126272A0001C6C87 /* resources.cpp in Sources */,
+ 8CD80D11126272A0001C6C87 /* scene.cpp in Sources */,
+ 8CD80D12126272A0001C6C87 /* segment.cpp in Sources */,
+ 8CD80D13126272A0001C6C87 /* surface.cpp in Sources */,
+ 8CD80D14126272A0001C6C87 /* surface_list.cpp in Sources */,
+ 8CD80D15126272A0001C6C87 /* teenagent.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -8394,7 +9008,6 @@
DF093F720F63CB26002D821E /* draw_bargon.cpp in Sources */,
DF093F730F63CB26002D821E /* draw_v1.cpp in Sources */,
DF093F740F63CB26002D821E /* draw_v2.cpp in Sources */,
- DF093F750F63CB26002D821E /* driver_vga.cpp in Sources */,
DF093F760F63CB26002D821E /* game.cpp in Sources */,
DF093F790F63CB26002D821E /* global.cpp in Sources */,
DF093F7A0F63CB26002D821E /* gob.cpp in Sources */,
@@ -8706,7 +9319,6 @@
DF0940D80F63CB26002D821E /* insane_enemy.cpp in Sources */,
DF0940D90F63CB26002D821E /* insane_iact.cpp in Sources */,
DF0940DA0F63CB26002D821E /* insane_scenes.cpp in Sources */,
- DF0940DB0F63CB26002D821E /* midiparser_eup.cpp in Sources */,
DF0940DC0F63CB26002D821E /* midiparser_ro.cpp in Sources */,
DF0940DD0F63CB26002D821E /* nut_renderer.cpp in Sources */,
DF0940DE0F63CB26002D821E /* object.cpp in Sources */,
@@ -8983,8 +9595,6 @@
DF6118490FE3A8250042AD3F /* decompressor.cpp in Sources */,
DF61184A0FE3A8250042AD3F /* resource.cpp in Sources */,
DF6118550FE3A8990042AD3F /* disk.cpp in Sources */,
- DF6118660FE3A9410042AD3F /* coktelvideo.cpp in Sources */,
- DF6118670FE3A9410042AD3F /* indeo3.cpp in Sources */,
DF6118680FE3A9410042AD3F /* dxa_decoder.cpp in Sources */,
DF6118690FE3A9410042AD3F /* flic_decoder.cpp in Sources */,
DF61186A0FE3A9410042AD3F /* smk_decoder.cpp in Sources */,
@@ -9064,12 +9674,10 @@
DF45B1FE116628A5009B85CC /* menu.cpp in Sources */,
DF45B1FF116628A5009B85CC /* paint.cpp in Sources */,
DF45B200116628A5009B85CC /* paint16.cpp in Sources */,
- DF45B201116628A5009B85CC /* paint32.cpp in Sources */,
DF45B202116628A5009B85CC /* palette.cpp in Sources */,
DF45B203116628A5009B85CC /* picture.cpp in Sources */,
DF45B204116628A5009B85CC /* portrait.cpp in Sources */,
DF45B205116628A5009B85CC /* ports.cpp in Sources */,
- DF45B206116628A5009B85CC /* robot.cpp in Sources */,
DF45B207116628A5009B85CC /* screen.cpp in Sources */,
DF45B208116628A5009B85CC /* text16.cpp in Sources */,
DF45B209116628A5009B85CC /* transitions.cpp in Sources */,
@@ -9086,7 +9694,6 @@
DF45B21A116628A5009B85CC /* music.cpp in Sources */,
DF45B21B116628A5009B85CC /* soundcmd.cpp in Sources */,
DF45B21C116628A5009B85CC /* seq_decoder.cpp in Sources */,
- DF45B21D116628A5009B85CC /* vmd_decoder.cpp in Sources */,
DFCDC6DB116629CE00A7D2A0 /* features.cpp in Sources */,
DFCDC6DC116629CE00A7D2A0 /* kparse.cpp in Sources */,
DFCDC6F811662AAB00A7D2A0 /* resource.cpp in Sources */,
@@ -9109,7 +9716,6 @@
DFB0578C11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579311B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579A11B7549C0015AE65 /* cinepak.cpp in Sources */,
- DF7F285F11FF23B700159131 /* frameout.cpp in Sources */,
DF7F286311FF23D500159131 /* amigamac.cpp in Sources */,
DF7F286B11FF23EF00159131 /* kvideo.cpp in Sources */,
DF7F286C11FF23EF00159131 /* workarounds.cpp in Sources */,
@@ -9122,6 +9728,79 @@
DF7F289711FF247300159131 /* translation.cpp in Sources */,
DF7F28A211FF24B000159131 /* mac_snd.cpp in Sources */,
DF7F28A711FF24C400159131 /* console.cpp in Sources */,
+ DF895BFF124C24350077F6E8 /* coktel_decoder.cpp in Sources */,
+ DF895C04124C24680077F6E8 /* player_towns.cpp in Sources */,
+ DF895C0A124C24B60077F6E8 /* appleiigs.cpp in Sources */,
+ DF895C19124C24C10077F6E8 /* towns_audio.cpp in Sources */,
+ DF895C1A124C24C10077F6E8 /* towns_euphony.cpp in Sources */,
+ DF895C1B124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */,
+ DF895C1C124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */,
+ DF895C26124C25150077F6E8 /* init_fascin.cpp in Sources */,
+ DF895C2B124C25350077F6E8 /* script_patches.cpp in Sources */,
+ DF895CBE124E58990077F6E8 /* indeo3.cpp in Sources */,
+ DF895CBF124E58990077F6E8 /* mjpeg.cpp in Sources */,
+ DF895CC0124E58990077F6E8 /* qdm2.cpp in Sources */,
+ DF895CC1124E58990077F6E8 /* qtrle.cpp in Sources */,
+ DF895CC2124E58990077F6E8 /* rpza.cpp in Sources */,
+ DF895CC3124E58990077F6E8 /* smc.cpp in Sources */,
+ DF0E303C1252C5BD0082D593 /* cms.cpp in Sources */,
+ DF0E30431252C6090082D593 /* cms.cpp in Sources */,
+ 8CB5A9C61253FD6900CB6BC7 /* m4_scene.cpp in Sources */,
+ 8CB5A9C71253FD6900CB6BC7 /* mads_logic.cpp in Sources */,
+ 8CB5A9C81253FD6900CB6BC7 /* mads_player.cpp in Sources */,
+ 8CB5A9C91253FD6900CB6BC7 /* mads_scene.cpp in Sources */,
+ 8CB5A9CA1253FD6900CB6BC7 /* mads_views.cpp in Sources */,
+ 8CD1ED2F126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED30126202AB00FA198C /* display.cpp in Sources */,
+ 8CD1ED31126202AB00FA198C /* engine.cpp in Sources */,
+ 8CD1ED32126202AB00FA198C /* file.cpp in Sources */,
+ 8CD1ED33126202AB00FA198C /* hugo.cpp in Sources */,
+ 8CD1ED34126202AB00FA198C /* intro.cpp in Sources */,
+ 8CD1ED35126202AB00FA198C /* inventory.cpp in Sources */,
+ 8CD1ED38126202AB00FA198C /* mouse.cpp in Sources */,
+ 8CD1ED39126202AB00FA198C /* parser.cpp in Sources */,
+ 8CD1ED3A126202AB00FA198C /* route.cpp in Sources */,
+ 8CD1ED3B126202AB00FA198C /* schedule.cpp in Sources */,
+ 8CD1ED3C126202AB00FA198C /* sound.cpp in Sources */,
+ 8CD1ED3D126202AB00FA198C /* util.cpp in Sources */,
+ 8CD1ED3E126202AB00FA198C /* anim.cpp in Sources */,
+ 8CD1ED3F126202AB00FA198C /* audio.cpp in Sources */,
+ 8CD1ED40126202AB00FA198C /* character.cpp in Sources */,
+ 8CD1ED41126202AB00FA198C /* conversation.cpp in Sources */,
+ 8CD1ED42126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED43126202AB00FA198C /* drew.cpp in Sources */,
+ 8CD1ED44126202AB00FA198C /* flux.cpp in Sources */,
+ 8CD1ED45126202AB00FA198C /* font.cpp in Sources */,
+ 8CD1ED46126202AB00FA198C /* hotspot.cpp in Sources */,
+ 8CD1ED49126202AB00FA198C /* movie.cpp in Sources */,
+ 8CD1ED4A126202AB00FA198C /* path.cpp in Sources */,
+ 8CD1ED4B126202AB00FA198C /* picture.cpp in Sources */,
+ 8CD1ED4C126202AB00FA198C /* resource.cpp in Sources */,
+ 8CD1ED4D126202AB00FA198C /* script.cpp in Sources */,
+ 8CD1ED4E126202AB00FA198C /* script_func.cpp in Sources */,
+ 8CD1ED4F126202AB00FA198C /* state.cpp in Sources */,
+ 8CD1ED50126202AB00FA198C /* text.cpp in Sources */,
+ 8CD1ED51126202AB00FA198C /* tools.cpp in Sources */,
+ 8CD1ED52126202AB00FA198C /* toon.cpp in Sources */,
+ 8CD80C8C126271A9001C6C87 /* surface.cpp in Sources */,
+ 8CD80C92126271BD001C6C87 /* gfx_towns.cpp in Sources */,
+ 8CD80CF2126272A0001C6C87 /* actor.cpp in Sources */,
+ 8CD80CF3126272A0001C6C87 /* animation.cpp in Sources */,
+ 8CD80CF4126272A0001C6C87 /* callbacks.cpp in Sources */,
+ 8CD80CF5126272A0001C6C87 /* console.cpp in Sources */,
+ 8CD80CF6126272A0001C6C87 /* detection.cpp in Sources */,
+ 8CD80CF7126272A0001C6C87 /* dialog.cpp in Sources */,
+ 8CD80CF8126272A0001C6C87 /* font.cpp in Sources */,
+ 8CD80CF9126272A0001C6C87 /* inventory.cpp in Sources */,
+ 8CD80CFB126272A0001C6C87 /* music.cpp in Sources */,
+ 8CD80CFC126272A0001C6C87 /* objects.cpp in Sources */,
+ 8CD80CFD126272A0001C6C87 /* pack.cpp in Sources */,
+ 8CD80CFE126272A0001C6C87 /* resources.cpp in Sources */,
+ 8CD80CFF126272A0001C6C87 /* scene.cpp in Sources */,
+ 8CD80D00126272A0001C6C87 /* segment.cpp in Sources */,
+ 8CD80D01126272A0001C6C87 /* surface.cpp in Sources */,
+ 8CD80D02126272A0001C6C87 /* surface_list.cpp in Sources */,
+ 8CD80D03126272A0001C6C87 /* teenagent.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -9363,7 +10042,6 @@
DFF95A080FB22D5700A3EC78 /* draw_bargon.cpp in Sources */,
DFF95A090FB22D5700A3EC78 /* draw_v1.cpp in Sources */,
DFF95A0A0FB22D5700A3EC78 /* draw_v2.cpp in Sources */,
- DFF95A0B0FB22D5700A3EC78 /* driver_vga.cpp in Sources */,
DFF95A0C0FB22D5700A3EC78 /* game.cpp in Sources */,
DFF95A0F0FB22D5700A3EC78 /* global.cpp in Sources */,
DFF95A100FB22D5700A3EC78 /* gob.cpp in Sources */,
@@ -9675,7 +10353,6 @@
DFF95B6D0FB22D5700A3EC78 /* insane_enemy.cpp in Sources */,
DFF95B6E0FB22D5700A3EC78 /* insane_iact.cpp in Sources */,
DFF95B6F0FB22D5700A3EC78 /* insane_scenes.cpp in Sources */,
- DFF95B700FB22D5700A3EC78 /* midiparser_eup.cpp in Sources */,
DFF95B710FB22D5700A3EC78 /* midiparser_ro.cpp in Sources */,
DFF95B720FB22D5700A3EC78 /* nut_renderer.cpp in Sources */,
DFF95B730FB22D5700A3EC78 /* object.cpp in Sources */,
@@ -9947,8 +10624,6 @@
DF61184F0FE3A8250042AD3F /* decompressor.cpp in Sources */,
DF6118500FE3A8250042AD3F /* resource.cpp in Sources */,
DF6118570FE3A8990042AD3F /* disk.cpp in Sources */,
- DF6118700FE3A9410042AD3F /* coktelvideo.cpp in Sources */,
- DF6118710FE3A9410042AD3F /* indeo3.cpp in Sources */,
DF6118720FE3A9410042AD3F /* dxa_decoder.cpp in Sources */,
DF6118730FE3A9410042AD3F /* flic_decoder.cpp in Sources */,
DF6118740FE3A9410042AD3F /* smk_decoder.cpp in Sources */,
@@ -10030,12 +10705,10 @@
DF45B228116628A5009B85CC /* menu.cpp in Sources */,
DF45B229116628A5009B85CC /* paint.cpp in Sources */,
DF45B22A116628A5009B85CC /* paint16.cpp in Sources */,
- DF45B22B116628A5009B85CC /* paint32.cpp in Sources */,
DF45B22C116628A5009B85CC /* palette.cpp in Sources */,
DF45B22D116628A5009B85CC /* picture.cpp in Sources */,
DF45B22E116628A5009B85CC /* portrait.cpp in Sources */,
DF45B22F116628A5009B85CC /* ports.cpp in Sources */,
- DF45B230116628A5009B85CC /* robot.cpp in Sources */,
DF45B231116628A5009B85CC /* screen.cpp in Sources */,
DF45B232116628A5009B85CC /* text16.cpp in Sources */,
DF45B233116628A5009B85CC /* transitions.cpp in Sources */,
@@ -10052,7 +10725,6 @@
DF45B244116628A5009B85CC /* music.cpp in Sources */,
DF45B245116628A5009B85CC /* soundcmd.cpp in Sources */,
DF45B246116628A5009B85CC /* seq_decoder.cpp in Sources */,
- DF45B247116628A5009B85CC /* vmd_decoder.cpp in Sources */,
DFCDC6DD116629CE00A7D2A0 /* features.cpp in Sources */,
DFCDC6DE116629CE00A7D2A0 /* kparse.cpp in Sources */,
DFCDC6F911662AAB00A7D2A0 /* resource.cpp in Sources */,
@@ -10075,7 +10747,6 @@
DFB0578A11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579111B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579811B7549C0015AE65 /* cinepak.cpp in Sources */,
- DF7F285D11FF23B700159131 /* frameout.cpp in Sources */,
DF7F286111FF23D500159131 /* amigamac.cpp in Sources */,
DF7F286711FF23EF00159131 /* kvideo.cpp in Sources */,
DF7F286811FF23EF00159131 /* workarounds.cpp in Sources */,
@@ -10088,6 +10759,79 @@
DF7F289311FF247300159131 /* translation.cpp in Sources */,
DF7F28A011FF24B000159131 /* mac_snd.cpp in Sources */,
DF7F28A511FF24C400159131 /* console.cpp in Sources */,
+ DF895C00124C24350077F6E8 /* coktel_decoder.cpp in Sources */,
+ DF895C05124C24680077F6E8 /* player_towns.cpp in Sources */,
+ DF895C0B124C24B60077F6E8 /* appleiigs.cpp in Sources */,
+ DF895C1D124C24C10077F6E8 /* towns_audio.cpp in Sources */,
+ DF895C1E124C24C10077F6E8 /* towns_euphony.cpp in Sources */,
+ DF895C1F124C24C10077F6E8 /* towns_pc98_driver.cpp in Sources */,
+ DF895C20124C24C10077F6E8 /* towns_pc98_fmsynth.cpp in Sources */,
+ DF895C27124C25150077F6E8 /* init_fascin.cpp in Sources */,
+ DF895C2C124C25350077F6E8 /* script_patches.cpp in Sources */,
+ DF895CC4124E58990077F6E8 /* indeo3.cpp in Sources */,
+ DF895CC5124E58990077F6E8 /* mjpeg.cpp in Sources */,
+ DF895CC6124E58990077F6E8 /* qdm2.cpp in Sources */,
+ DF895CC7124E58990077F6E8 /* qtrle.cpp in Sources */,
+ DF895CC8124E58990077F6E8 /* rpza.cpp in Sources */,
+ DF895CC9124E58990077F6E8 /* smc.cpp in Sources */,
+ DF0E303A1252C5BD0082D593 /* cms.cpp in Sources */,
+ DF0E30411252C6090082D593 /* cms.cpp in Sources */,
+ 8CB5A9C11253FD6900CB6BC7 /* m4_scene.cpp in Sources */,
+ 8CB5A9C21253FD6900CB6BC7 /* mads_logic.cpp in Sources */,
+ 8CB5A9C31253FD6900CB6BC7 /* mads_player.cpp in Sources */,
+ 8CB5A9C41253FD6900CB6BC7 /* mads_scene.cpp in Sources */,
+ 8CB5A9C51253FD6900CB6BC7 /* mads_views.cpp in Sources */,
+ 8CD1ED0B126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED0C126202AB00FA198C /* display.cpp in Sources */,
+ 8CD1ED0D126202AB00FA198C /* engine.cpp in Sources */,
+ 8CD1ED0E126202AB00FA198C /* file.cpp in Sources */,
+ 8CD1ED0F126202AB00FA198C /* hugo.cpp in Sources */,
+ 8CD1ED10126202AB00FA198C /* intro.cpp in Sources */,
+ 8CD1ED11126202AB00FA198C /* inventory.cpp in Sources */,
+ 8CD1ED14126202AB00FA198C /* mouse.cpp in Sources */,
+ 8CD1ED15126202AB00FA198C /* parser.cpp in Sources */,
+ 8CD1ED16126202AB00FA198C /* route.cpp in Sources */,
+ 8CD1ED17126202AB00FA198C /* schedule.cpp in Sources */,
+ 8CD1ED18126202AB00FA198C /* sound.cpp in Sources */,
+ 8CD1ED19126202AB00FA198C /* util.cpp in Sources */,
+ 8CD1ED1A126202AB00FA198C /* anim.cpp in Sources */,
+ 8CD1ED1B126202AB00FA198C /* audio.cpp in Sources */,
+ 8CD1ED1C126202AB00FA198C /* character.cpp in Sources */,
+ 8CD1ED1D126202AB00FA198C /* conversation.cpp in Sources */,
+ 8CD1ED1E126202AB00FA198C /* detection.cpp in Sources */,
+ 8CD1ED1F126202AB00FA198C /* drew.cpp in Sources */,
+ 8CD1ED20126202AB00FA198C /* flux.cpp in Sources */,
+ 8CD1ED21126202AB00FA198C /* font.cpp in Sources */,
+ 8CD1ED22126202AB00FA198C /* hotspot.cpp in Sources */,
+ 8CD1ED25126202AB00FA198C /* movie.cpp in Sources */,
+ 8CD1ED26126202AB00FA198C /* path.cpp in Sources */,
+ 8CD1ED27126202AB00FA198C /* picture.cpp in Sources */,
+ 8CD1ED28126202AB00FA198C /* resource.cpp in Sources */,
+ 8CD1ED29126202AB00FA198C /* script.cpp in Sources */,
+ 8CD1ED2A126202AB00FA198C /* script_func.cpp in Sources */,
+ 8CD1ED2B126202AB00FA198C /* state.cpp in Sources */,
+ 8CD1ED2C126202AB00FA198C /* text.cpp in Sources */,
+ 8CD1ED2D126202AB00FA198C /* tools.cpp in Sources */,
+ 8CD1ED2E126202AB00FA198C /* toon.cpp in Sources */,
+ 8CD80C8B126271A9001C6C87 /* surface.cpp in Sources */,
+ 8CD80C91126271BD001C6C87 /* gfx_towns.cpp in Sources */,
+ 8CD80CE0126272A0001C6C87 /* actor.cpp in Sources */,
+ 8CD80CE1126272A0001C6C87 /* animation.cpp in Sources */,
+ 8CD80CE2126272A0001C6C87 /* callbacks.cpp in Sources */,
+ 8CD80CE3126272A0001C6C87 /* console.cpp in Sources */,
+ 8CD80CE4126272A0001C6C87 /* detection.cpp in Sources */,
+ 8CD80CE5126272A0001C6C87 /* dialog.cpp in Sources */,
+ 8CD80CE6126272A0001C6C87 /* font.cpp in Sources */,
+ 8CD80CE7126272A0001C6C87 /* inventory.cpp in Sources */,
+ 8CD80CE9126272A0001C6C87 /* music.cpp in Sources */,
+ 8CD80CEA126272A0001C6C87 /* objects.cpp in Sources */,
+ 8CD80CEB126272A0001C6C87 /* pack.cpp in Sources */,
+ 8CD80CEC126272A0001C6C87 /* resources.cpp in Sources */,
+ 8CD80CED126272A0001C6C87 /* scene.cpp in Sources */,
+ 8CD80CEE126272A0001C6C87 /* segment.cpp in Sources */,
+ 8CD80CEF126272A0001C6C87 /* surface.cpp in Sources */,
+ 8CD80CF0126272A0001C6C87 /* surface_list.cpp in Sources */,
+ 8CD80CF1126272A0001C6C87 /* teenagent.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -10110,7 +10854,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
- GCC_OPTIMIZATION_LEVEL = 3;
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
GCC_THUMB_SUPPORT = NO;
@@ -10123,9 +10867,6 @@
INFOPLIST_FILE = Info.plist;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
- "\"$(SRCROOT)/../../sound/softsynth/mt32\"",
- "\"$(SRCROOT)/../../engines/cruise\"",
- "\"$(SRCROOT)/../../engines/m4\"",
"\"$(SRCROOT)/lib\"",
);
ONLY_ACTIVE_ARCH = YES;
@@ -10133,7 +10874,7 @@
PRODUCT_NAME = ScummVM;
PROVISIONING_PROFILE = "EF590570-5FAC-4346-9071-D609DE2B28D8";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
- SDKROOT = iphoneos3.2;
+ SDKROOT = iphoneos4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
@@ -10166,9 +10907,6 @@
INFOPLIST_FILE = Info.plist;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
- "\"$(SRCROOT)/../../sound/softsynth/mt32\"",
- "\"$(SRCROOT)/../../engines/cruise\"",
- "\"$(SRCROOT)/../../engines/m4\"",
"\"$(SRCROOT)/lib\"",
);
ONLY_ACTIVE_ARCH = YES;
@@ -10194,45 +10932,45 @@
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_INPUT_FILETYPE = automatic;
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
- XCODE,
+ CONFIG_H,
IPHONE_OFFICIAL,
- USE_VORBIS,
- USE_FLAC,
- USE_TREMOR,
- USE_MAD,
- USE_ZLIB,
- SCUMMVM,
- ENABLE_SCI,
- ENABLE_TUCKER,
- ENABLE_TOUCHE,
- ENABLE_TINSEL,
- ENABLE_SWORD2,
- ENABLE_SWORD1,
- ENABLE_SKY,
- ENABLE_IHNM,
- ENABLE_SAGA,
- ENABLE_QUEEN,
- ENABLE_PARALLACTION,
- ENABLE_MADE,
- ENABLE_LURE,
- ENABLE_KYRA,
- ENABLE_IGOR,
- ENABLE_GROOVIE,
- ENABLE_GOB,
- ENABLE_DRASCULA,
- ENABLE_CINE,
- ENABLE_AGOS,
- ENABLE_AGI,
+ IPHONE,
+ UNIX,
+ SCUMM_LITTLE_ENDIAN,
+ SCUMM_NEED_ALIGNMENT,
ENABLE_SCUMM,
ENABLE_SCUMM_7_8,
- CONFIG_H,
- SCUMM_NEED_ALIGNMENT,
- SCUMM_LITTLE_ENDIAN,
- UNIX,
- IPHONE,
- ENABLE_SCI32,
ENABLE_HE,
+ ENABLE_AGI,
+ ENABLE_AGOS,
+ ENABLE_CINE,
+ ENABLE_CRUISE,
+ ENABLE_DRASCULA,
+ ENABLE_GOB,
+ ENABLE_GROOVIE,
+ ENABLE_IGOR,
+ ENABLE_KYRA,
+ ENABLE_LURE,
+ ENABLE_MADE,
+ ENABLE_PARALLACTION,
+ ENABLE_QUEEN,
+ ENABLE_SAGA,
+ ENABLE_IHNM,
+ ENABLE_SCI,
+ ENABLE_SKY,
+ ENABLE_SWORD1,
+ ENABLE_SWORD2,
+ ENABLE_TEENAGENT,
+ ENABLE_TINSEL,
+ ENABLE_TOUCHE,
+ ENABLE_TUCKER,
+ USE_FLAC,
+ USE_MAD,
+ USE_TREMOR,
+ USE_VORBIS,
+ USE_ZLIB,
);
GCC_THUMB_SUPPORT = NO;
GCC_USE_GCC3_PFE_SUPPORT = NO;
@@ -10249,8 +10987,9 @@
PREBINDING = NO;
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
- SDKROOT = iphonesimulator3.2;
+ SDKROOT = iphoneos4.0;
TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64 armv6 armv7";
};
name = Debug;
};
@@ -10265,62 +11004,77 @@
GCC_C_LANGUAGE_STANDARD = c99;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
+ GCC_ENABLE_EXCEPTIONS = NO;
GCC_INPUT_FILETYPE = automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
- XCODE,
+ CONFIG_H,
IPHONE_OFFICIAL,
- USE_VORBIS,
- USE_FLAC,
- USE_TREMOR,
- USE_MAD,
- USE_ZLIB,
- SCUMMVM,
- ENABLE_SCI,
- ENABLE_TUCKER,
- ENABLE_TOUCHE,
- ENABLE_TINSEL,
- ENABLE_SWORD2,
- ENABLE_SWORD1,
- ENABLE_SKY,
- ENABLE_IHNM,
- ENABLE_SAGA,
- ENABLE_QUEEN,
- ENABLE_PARALLACTION,
- ENABLE_MADE,
- ENABLE_LURE,
- ENABLE_KYRA,
- ENABLE_IGOR,
- ENABLE_GROOVIE,
- ENABLE_GOB,
- ENABLE_DRASCULA,
- ENABLE_CINE,
- ENABLE_AGOS,
- ENABLE_AGI,
+ IPHONE,
+ UNIX,
+ SCUMM_LITTLE_ENDIAN,
+ SCUMM_NEED_ALIGNMENT,
ENABLE_SCUMM,
ENABLE_SCUMM_7_8,
- CONFIG_H,
- SCUMM_NEED_ALIGNMENT,
- SCUMM_LITTLE_ENDIAN,
- UNIX,
- IPHONE,
- ENABLE_SCI32,
ENABLE_HE,
+ ENABLE_AGI,
+ ENABLE_AGOS,
+ ENABLE_CINE,
+ ENABLE_CRUISE,
+ ENABLE_DRASCULA,
+ ENABLE_GOB,
+ ENABLE_GROOVIE,
+ ENABLE_IGOR,
+ ENABLE_KYRA,
+ ENABLE_LURE,
+ ENABLE_MADE,
+ ENABLE_PARALLACTION,
+ ENABLE_QUEEN,
+ ENABLE_SAGA,
+ ENABLE_IHNM,
+ ENABLE_SCI,
+ ENABLE_SKY,
+ ENABLE_SWORD1,
+ ENABLE_SWORD2,
+ ENABLE_TEENAGENT,
+ ENABLE_TINSEL,
+ ENABLE_TOUCHE,
+ ENABLE_TUCKER,
+ USE_FLAC,
+ USE_MAD,
+ USE_TREMOR,
+ USE_VORBIS,
+ USE_ZLIB,
);
GCC_THUMB_SUPPORT = NO;
GCC_USE_GCC3_PFE_SUPPORT = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
HEADER_SEARCH_PATHS = (
+ /opt/local/include/SDL,
+ /opt/local/include,
+ /sw/include/SDL,
+ /sw/include,
../../engines/,
../../,
);
LIBRARY_SEARCH_PATHS = "";
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "";
- OTHER_LDFLAGS = "-lz";
+ OTHER_LDFLAGS = (
+ "-lSDLmain",
+ "-logg",
+ "-lvorbisfile",
+ "-lvorbis",
+ "-lmad",
+ "-lFLAC",
+ "-lSDL",
+ "-lz",
+ );
PREBINDING = NO;
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
SDKROOT = iphonesimulator3.2;
TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64 armv6 armv7";
};
name = Release;
};
@@ -10338,48 +11092,51 @@
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
- USE_VORBIS,
- USE_FLAC,
- USE_TREMOR,
- USE_MPEG2,
- USE_MAD,
- USE_ZLIB,
- SCUMMVM,
- ENABLE_SCI,
- ENABLE_TUCKER,
- ENABLE_TOUCHE,
- ENABLE_TINSEL,
- ENABLE_SWORD2,
- ENABLE_SWORD1,
- ENABLE_SKY,
- ENABLE_IHNM,
- ENABLE_SAGA,
- ENABLE_QUEEN,
- ENABLE_PARALLACTION,
- ENABLE_MADE,
- ENABLE_LURE,
- ENABLE_KYRA,
- ENABLE_IGOR,
- ENABLE_GROOVIE,
- ENABLE_GOB,
- ENABLE_DRASCULA,
- ENABLE_CINE,
- ENABLE_AGOS,
- ENABLE_AGI,
- ENABLE_SCUMM,
- ENABLE_SCUMM_7_8,
CONFIG_H,
- SCUMM_NEED_ALIGNMENT,
- SCUMM_LITTLE_ENDIAN,
- UNIX,
- SDL_BACKEND,
MACOSX,
+ SDL_BACKEND,
+ UNIX,
+ SCUMM_LITTLE_ENDIAN,
+ SCUMM_NEED_ALIGNMENT,
+ ENABLE_SCUMM,
+ ENABLE_SCUMM_7_8,
ENABLE_HE,
+ ENABLE_AGI,
+ ENABLE_AGOS,
+ ENABLE_CINE,
+ ENABLE_CRUISE,
+ ENABLE_DRASCULA,
+ ENABLE_GOB,
+ ENABLE_GROOVIE,
+ ENABLE_IGOR,
+ ENABLE_KYRA,
+ ENABLE_LURE,
+ ENABLE_MADE,
+ ENABLE_PARALLACTION,
+ ENABLE_QUEEN,
+ ENABLE_SAGA,
+ ENABLE_IHNM,
+ ENABLE_SCI,
+ ENABLE_SKY,
+ ENABLE_SWORD1,
+ ENABLE_SWORD2,
+ ENABLE_TEENAGENT,
+ ENABLE_TINSEL,
+ ENABLE_TOUCHE,
+ ENABLE_TUCKER,
+ USE_FLAC,
+ USE_MAD,
+ USE_MPEG2,
+ USE_TREMOR,
+ USE_VORBIS,
+ USE_ZLIB,
);
GCC_VERSION = "";
HEADER_SEARCH_PATHS = (
/opt/local/include/SDL,
/opt/local/include,
+ /sw/include/SDL,
+ /sw/include,
include/,
../../engines/,
../../,
@@ -10389,10 +11146,6 @@
/sw/lib,
/opt/local/lib,
"$(inherited)",
- "\\\"$(SRCROOT)/../../sound/softsynth/mt32\\\"",
- "\\\"$(SRCROOT)/../../engines/cruise\\\"",
- "\\\"$(SRCROOT)/../../engines/m4\\\"",
- "\\\"$(SRCROOT)/lib\\\"",
);
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "";
@@ -10409,7 +11162,7 @@
PREBINDING = NO;
PRODUCT_NAME = ScummVM;
SDKROOT = macosx10.6;
- VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64 arm7 arm6";
+ VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64";
};
name = Debug;
};
@@ -10425,49 +11178,51 @@
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
- USE_VORBIS,
- USE_FLAC,
- USE_TREMOR,
- USE_MPEG2,
- USE_MAD,
- USE_ZLIB,
- SCUMMVM,
- ENABLE_SCI,
- ENABLE_TUCKER,
- ENABLE_TOUCHE,
- ENABLE_TINSEL,
- ENABLE_SWORD2,
- ENABLE_SWORD1,
- ENABLE_SKY,
- ENABLE_IHNM,
- ENABLE_SAGA,
- ENABLE_QUEEN,
- ENABLE_PARALLACTION,
- ENABLE_MADE,
- ENABLE_LURE,
- ENABLE_KYRA,
- ENABLE_IGOR,
- ENABLE_GROOVIE,
- ENABLE_GOB,
- ENABLE_DRASCULA,
- ENABLE_CINE,
- ENABLE_AGOS,
- ENABLE_AGI,
- ENABLE_SCUMM,
- ENABLE_SCUMM_7_8,
CONFIG_H,
- SCUMM_NEED_ALIGNMENT,
- SCUMM_LITTLE_ENDIAN,
- UNIX,
- SDL_BACKEND,
MACOSX,
+ SDL_BACKEND,
+ UNIX,
+ SCUMM_LITTLE_ENDIAN,
+ SCUMM_NEED_ALIGNMENT,
+ ENABLE_SCUMM,
+ ENABLE_SCUMM_7_8,
ENABLE_HE,
+ ENABLE_AGI,
+ ENABLE_AGOS,
+ ENABLE_CINE,
+ ENABLE_CRUISE,
+ ENABLE_DRASCULA,
+ ENABLE_GOB,
+ ENABLE_GROOVIE,
+ ENABLE_IGOR,
+ ENABLE_KYRA,
+ ENABLE_LURE,
+ ENABLE_MADE,
+ ENABLE_PARALLACTION,
+ ENABLE_QUEEN,
+ ENABLE_SAGA,
+ ENABLE_IHNM,
+ ENABLE_SCI,
+ ENABLE_SKY,
+ ENABLE_SWORD1,
+ ENABLE_SWORD2,
+ ENABLE_TEENAGENT,
+ ENABLE_TINSEL,
+ ENABLE_TOUCHE,
+ ENABLE_TUCKER,
+ USE_FLAC,
+ USE_MAD,
+ USE_MPEG2,
+ USE_TREMOR,
+ USE_VORBIS,
+ USE_ZLIB,
);
GCC_VERSION = "";
HEADER_SEARCH_PATHS = (
/opt/local/include/SDL,
/opt/local/include,
- include/,
+ /sw/include/SDL,
+ /sw/include,
../../engines/,
../../,
);
@@ -10476,10 +11231,6 @@
/sw/lib,
/opt/local/lib,
"$(inherited)",
- "\\\"$(SRCROOT)/../../sound/softsynth/mt32\\\"",
- "\\\"$(SRCROOT)/../../engines/cruise\\\"",
- "\\\"$(SRCROOT)/../../engines/m4\\\"",
- "\\\"$(SRCROOT)/lib\\\"",
);
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "";
@@ -10496,7 +11247,7 @@
PREBINDING = NO;
PRODUCT_NAME = ScummVM;
SDKROOT = macosx10.6;
- VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64 arm7 arm6";
+ VALID_ARCHS = "i386 ppc ppc64 ppc7400 ppc970 x86_64";
WRAPPER_EXTENSION = app;
};
name = Release;
@@ -10519,17 +11270,28 @@
GCC_THUMB_SUPPORT = NO;
GCC_UNROLL_LOOPS = YES;
HEADER_SEARCH_PATHS = (
+ /opt/local/include/SDL,
+ /opt/local/include,
+ /sw/include/SDL,
+ /sw/include,
../../engines/,
../../,
- /opt/local/include,
);
- INFOPLIST_FILE = "/Users/oystein/iphone/scummvm/dists/iphone/Info copy 2.plist";
+ INFOPLIST_FILE = Info.plist;
LIBRARY_SEARCH_PATHS = (
+ /sw/lib,
+ /opt/local/lib,
"$(inherited)",
- "\\\"$(SRCROOT)/../../sound/softsynth/mt32\\\"",
- "\\\"$(SRCROOT)/../../engines/cruise\\\"",
- "\\\"$(SRCROOT)/../../engines/m4\\\"",
- "\\\"$(SRCROOT)/lib\\\"",
+ );
+ OTHER_LDFLAGS = (
+ "-lSDLmain",
+ "-logg",
+ "-lvorbisfile",
+ "-lvorbis",
+ "-lmad",
+ "-lFLAC",
+ "-lSDL",
+ "-lz",
);
PREBINDING = NO;
PRODUCT_NAME = ScummVM;
@@ -10556,17 +11318,28 @@
GCC_THUMB_SUPPORT = NO;
GCC_UNROLL_LOOPS = YES;
HEADER_SEARCH_PATHS = (
+ /opt/local/include/SDL,
+ /opt/local/include,
+ /sw/include/SDL,
+ /sw/include,
../../engines/,
../../,
- /opt/local/include,
);
- INFOPLIST_FILE = "/Users/oystein/iphone/scummvm/dists/iphone/Info copy 2.plist";
+ INFOPLIST_FILE = Info.plist;
LIBRARY_SEARCH_PATHS = (
+ /sw/lib,
+ /opt/local/lib,
"$(inherited)",
- "\\\"$(SRCROOT)/../../sound/softsynth/mt32\\\"",
- "\\\"$(SRCROOT)/../../engines/cruise\\\"",
- "\\\"$(SRCROOT)/../../engines/m4\\\"",
- "\\\"$(SRCROOT)/lib\\\"",
+ );
+ OTHER_LDFLAGS = (
+ "-lSDLmain",
+ "-logg",
+ "-lvorbisfile",
+ "-lvorbis",
+ "-lmad",
+ "-lFLAC",
+ "-lSDL",
+ "-lz",
);
PREBINDING = NO;
PRODUCT_NAME = ScummVM;
diff --git a/dists/irix/scummvm.spec b/dists/irix/scummvm.spec
index 08dbf20826..f19a4716b7 100644
--- a/dists/irix/scummvm.spec
+++ b/dists/irix/scummvm.spec
@@ -1,5 +1,5 @@
product scummvm
- id "ScummVM 1.2.0svn"
+ id "ScummVM 1.3.0svn"
image sw
id "software"
version 18
diff --git a/dists/macosx/Info.plist b/dists/macosx/Info.plist
index 633c6b7e96..1a64925483 100644
--- a/dists/macosx/Info.plist
+++ b/dists/macosx/Info.plist
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>scummvm</string>
<key>CFBundleGetInfoString</key>
- <string>1.2.0svn, Copyright 2001-2010 The ScummVM team</string>
+ <string>1.3.0svn, Copyright 2001-2010 The ScummVM team</string>
<key>CFBundleIconFile</key>
<string>scummvm.icns</string>
<key>CFBundleIdentifier</key>
@@ -21,9 +21,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.2.0svn</string>
+ <string>1.3.0svn</string>
<key>CFBundleVersion</key>
- <string>1.2.0svn</string>
+ <string>1.3.0svn</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHumanReadableCopyright</key>
diff --git a/dists/macosx/Info.plist.in b/dists/macosx/Info.plist.in
index 26a044e5df..0eedf19919 100644
--- a/dists/macosx/Info.plist.in
+++ b/dists/macosx/Info.plist.in
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
diff --git a/dists/redhat/scummvm-tools.spec b/dists/redhat/scummvm-tools.spec
index 5721233ff6..2370468e28 100644
--- a/dists/redhat/scummvm-tools.spec
+++ b/dists/redhat/scummvm-tools.spec
@@ -7,7 +7,7 @@
# Prologue information
#------------------------------------------------------------------------------
Name : scummvm-tools
-Version : 1.2.0svn
+Version : 1.3.0svn
Release : 1
Summary : ScummVM-related tools
Group : Interpreters
@@ -37,14 +37,13 @@ Tools for compressing ScummVM datafiles and other related tools.
(cd libmad-0.15.1b; grep -v 'force-\(mem\|addr\)' configure > configure.new; mv -f configure.new configure; chmod 700 configure; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp
make
-echo -e " This script is installed as\n "%{_datadir}/scummvm-tools/convert_dxa.sh.sample >> README
%install
install -m755 -d %{buildroot}%{_bindir}
+install -m755 -D create_sjisfnt %{buildroot}%{_bindir}
install -m755 -D scummvm-tools{,-cli} %{buildroot}%{_bindir}
install -m755 -D de{cine,gob,kyra,riven,scumm,sword2} %{buildroot}%{_bindir}
install -m755 -D {construct,extract}_mohawk %{buildroot}%{_bindir}
-install -m644 -D convert_dxa.sh %{buildroot}%{_datadir}/scummvm-tools/convert_dxa.sh.sample
%clean
rm -Rf ${RPM_BUILD_ROOT}
@@ -55,10 +54,10 @@ rm -Rf ${RPM_BUILD_ROOT}
%files
%doc README COPYING
%attr(0755,root,root)%{_bindir}/scummvm*
+%attr(0755,root,root)%{_bindir}/create_sjisfnt
%attr(0755,root,root)%{_bindir}/de*
%attr(0755,root,root)%{_bindir}/extract_*
%attr(0755,root,root)%{_bindir}/construct_*
-%attr(0644,root,root)%{_datadir}/scummvm-tools/convert_dxa.sh.sample
#------------------------------------------------------------------------------
# Change Log
diff --git a/dists/redhat/scummvm-tools.spec.in b/dists/redhat/scummvm-tools.spec.in
index 56a4000755..f64871486d 100644
--- a/dists/redhat/scummvm-tools.spec.in
+++ b/dists/redhat/scummvm-tools.spec.in
@@ -37,14 +37,13 @@ Tools for compressing ScummVM datafiles and other related tools.
(cd libmad-0.15.1b; grep -v 'force-\(mem\|addr\)' configure > configure.new; mv -f configure.new configure; chmod 700 configure; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp
make
-echo -e " This script is installed as\n "%{_datadir}/scummvm-tools/convert_dxa.sh.sample >> README
%install
install -m755 -d %{buildroot}%{_bindir}
+install -m755 -D create_sjisfnt %{buildroot}%{_bindir}
install -m755 -D scummvm-tools{,-cli} %{buildroot}%{_bindir}
install -m755 -D de{cine,gob,kyra,riven,scumm,sword2} %{buildroot}%{_bindir}
install -m755 -D {construct,extract}_mohawk %{buildroot}%{_bindir}
-install -m644 -D convert_dxa.sh %{buildroot}%{_datadir}/scummvm-tools/convert_dxa.sh.sample
%clean
rm -Rf ${RPM_BUILD_ROOT}
@@ -55,10 +54,10 @@ rm -Rf ${RPM_BUILD_ROOT}
%files
%doc README COPYING
%attr(0755,root,root)%{_bindir}/scummvm*
+%attr(0755,root,root)%{_bindir}/create_sjisfnt
%attr(0755,root,root)%{_bindir}/de*
%attr(0755,root,root)%{_bindir}/extract_*
%attr(0755,root,root)%{_bindir}/construct_*
-%attr(0644,root,root)%{_datadir}/scummvm-tools/convert_dxa.sh.sample
#------------------------------------------------------------------------------
# Change Log
diff --git a/dists/redhat/scummvm.spec b/dists/redhat/scummvm.spec
index 1212e8b61b..950acf2a74 100644
--- a/dists/redhat/scummvm.spec
+++ b/dists/redhat/scummvm.spec
@@ -7,7 +7,7 @@
# Prologue information
#------------------------------------------------------------------------------
Name : scummvm
-Version : 1.2.0svn
+Version : 1.3.0svn
Release : 1
Summary : Graphic adventure game interpreter
Group : Interpreters
@@ -17,7 +17,6 @@ Url : http://www.scummvm.org
Source : %{name}-%{version}.tar.bz2
Source1 : libmad-0.15.1b.tar.bz2
-Source2 : mpeg2dec-0.4.1.tar.bz2
BuildRoot : %{_tmppath}/%{name}-%{version}-root
BuildRequires: desktop-file-utils
@@ -32,34 +31,36 @@ BuildRequires: SDL-devel >= 1.2.2
# Description
#------------------------------------------------------------------------------
%description
-ScummVM is an interpreter that will play graphic adventure games written for
-LucasArts' SCUMM virtual machine (such as Day of the Tentacle and
-Monkey Island), Sierra's AGI adventures (such as early King's Quest and
-Space Quest games), Adventure Soft's Simon the Sorcerer 1, 2 and Feeble Files,
-Revolution Software's Beneath a Steel Sky and Broken Sword I and II,
-Interactive Binary Illusions' Flight of the Amazon Queen,
-Coktel Vision's Gobliiins, Wyrmkeep's Inherit the Earth, Westwood's
-Legend of Kyrandia, and various others.
+ScummVM is an interpreter that will play many graphic adventure games,
+including LucasArts SCUMM games (such as Monkey Island 1-3, Day of the
+Tentacle, Sam & Max, ...), many of Sierra's AGI and SCI games (such as King's
+Quest 1-6, Space Quest 1-5, ...), Discworld 1 and 2, Simon the Sorcerer 1 and
+2, Beneath A Steel Sky, Lure of the Temptress, Broken Sword 1 and 2, Flight of
+the Amazon Queen, Gobliiins 1-3, The Legend of Kyrandia 1-3, many of Humongous
+Entertainment's children's SCUMM games (including Freddi Fish and Putt Putt
+games) and many more. See http://www.scummvm.org for a full compatibility list.
#------------------------------------------------------------------------------
# install scripts
#------------------------------------------------------------------------------
%prep
-%setup -q -a 1 -a 2 -n scummvm-%{version}
+%setup -q -a 1 -n scummvm-%{version}
mkdir tmp
%build
(cd libmad-0.15.1b; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
-(cd mpeg2dec-0.4.1; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
-./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp --with-mpeg2-prefix=%{_builddir}/scummvm-%{version}/tmp --prefix=%{_prefix} --enable-release
+./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp --prefix=%{_prefix} --enable-release
make
%install
install -m755 -D scummvm %{buildroot}%{_bindir}/scummvm
install -m644 -D dists/scummvm.6 %{buildroot}%{_mandir}/man6/scummvm.6
install -m644 -D icons/scummvm.xpm %{buildroot}%{_datadir}/pixmaps/scummvm.xpm
+install -m644 -D icons/scummvm.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/scummvm.svg
+install -m644 -D dists/redhat/scummvm48.png %{buildroot}%{_datadir}/icons/hicolor/48x48/apps/scummvm.png
install -m644 -D gui/themes/scummclassic.zip %{buildroot}%{_datadir}/scummvm/scummclassic.zip
install -m644 -D gui/themes/scummmodern.zip %{buildroot}%{_datadir}/scummvm/scummmodern.zip
+install -m644 -D gui/themes/translations.dat %{buildroot}%{_datadir}/scummvm/translations.dat
install -m644 -D dists/pred.dic %{buildroot}%{_datadir}/scummvm/pred.dic
install -m644 -D dists/engine-data/kyra.dat %{buildroot}%{_datadir}/scummvm/kyra.dat
install -m644 -D dists/engine-data/lure.dat %{buildroot}%{_datadir}/scummvm/lure.dat
@@ -72,16 +73,31 @@ desktop-file-install --vendor scummvm --dir=%{buildroot}/%{_datadir}/application
%clean
rm -Rf ${RPM_BUILD_ROOT}
+%post
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
+%postun
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
#------------------------------------------------------------------------------
# Files listing.
#------------------------------------------------------------------------------
%files
%defattr(0644,root,root,0755)
-%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYRIGHT
+%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.BSD COPYRIGHT
%attr(0755,root,root)%{_bindir}/scummvm
%{_datadir}/applications/*
%{_datadir}/pixmaps/scummvm.xpm
+%{_datadir}/icons/hicolor/48x48/apps/scummvm.png
+%{_datadir}/icons/hicolor/scalable/apps/scummvm.svg
%{_datadir}/scummvm/scumm*.zip
+%{_datadir}/scummvm/translations.dat
%{_datadir}/scummvm/pred.dic
%{_datadir}/scummvm/kyra.dat
%{_datadir}/scummvm/queen.tbl
@@ -95,6 +111,9 @@ rm -Rf ${RPM_BUILD_ROOT}
# Change Log
#------------------------------------------------------------------------------
%changelog
+* Fri Sep 17 2010 (1.2.0)
+ - include png/svg icons
+ - remove libmpeg2
* Thu Sep 21 2006 (0.9.1)
- include modern theme
* Mon Dec 20 2004 (0.7.0)
diff --git a/dists/redhat/scummvm.spec.in b/dists/redhat/scummvm.spec.in
index 89bb7692fb..13ce600d02 100644
--- a/dists/redhat/scummvm.spec.in
+++ b/dists/redhat/scummvm.spec.in
@@ -17,7 +17,6 @@ Url : http://www.scummvm.org
Source : %{name}-%{version}.tar.bz2
Source1 : libmad-0.15.1b.tar.bz2
-Source2 : mpeg2dec-0.4.1.tar.bz2
BuildRoot : %{_tmppath}/%{name}-%{version}-root
BuildRequires: desktop-file-utils
@@ -32,34 +31,36 @@ BuildRequires: SDL-devel >= 1.2.2
# Description
#------------------------------------------------------------------------------
%description
-ScummVM is an interpreter that will play graphic adventure games written for
-LucasArts' SCUMM virtual machine (such as Day of the Tentacle and
-Monkey Island), Sierra's AGI adventures (such as early King's Quest and
-Space Quest games), Adventure Soft's Simon the Sorcerer 1, 2 and Feeble Files,
-Revolution Software's Beneath a Steel Sky and Broken Sword I and II,
-Interactive Binary Illusions' Flight of the Amazon Queen,
-Coktel Vision's Gobliiins, Wyrmkeep's Inherit the Earth, Westwood's
-Legend of Kyrandia, and various others.
+ScummVM is an interpreter that will play many graphic adventure games,
+including LucasArts SCUMM games (such as Monkey Island 1-3, Day of the
+Tentacle, Sam & Max, ...), many of Sierra's AGI and SCI games (such as King's
+Quest 1-6, Space Quest 1-5, ...), Discworld 1 and 2, Simon the Sorcerer 1 and
+2, Beneath A Steel Sky, Lure of the Temptress, Broken Sword 1 and 2, Flight of
+the Amazon Queen, Gobliiins 1-3, The Legend of Kyrandia 1-3, many of Humongous
+Entertainment's children's SCUMM games (including Freddi Fish and Putt Putt
+games) and many more. See http://www.scummvm.org for a full compatibility list.
#------------------------------------------------------------------------------
# install scripts
#------------------------------------------------------------------------------
%prep
-%setup -q -a 1 -a 2 -n scummvm-%{version}
+%setup -q -a 1 -n scummvm-%{version}
mkdir tmp
%build
(cd libmad-0.15.1b; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
-(cd mpeg2dec-0.4.1; ./configure --enable-static --disable-shared --prefix=%{_builddir}/scummvm-%{version}/tmp; make; make install)
-./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp --with-mpeg2-prefix=%{_builddir}/scummvm-%{version}/tmp --prefix=%{_prefix} --enable-release
+./configure --with-mad-prefix=%{_builddir}/scummvm-%{version}/tmp --prefix=%{_prefix} --enable-release
make
%install
install -m755 -D scummvm %{buildroot}%{_bindir}/scummvm
install -m644 -D dists/scummvm.6 %{buildroot}%{_mandir}/man6/scummvm.6
install -m644 -D icons/scummvm.xpm %{buildroot}%{_datadir}/pixmaps/scummvm.xpm
+install -m644 -D icons/scummvm.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/scummvm.svg
+install -m644 -D dists/redhat/scummvm48.png %{buildroot}%{_datadir}/icons/hicolor/48x48/apps/scummvm.png
install -m644 -D gui/themes/scummclassic.zip %{buildroot}%{_datadir}/scummvm/scummclassic.zip
install -m644 -D gui/themes/scummmodern.zip %{buildroot}%{_datadir}/scummvm/scummmodern.zip
+install -m644 -D gui/themes/translations.dat %{buildroot}%{_datadir}/scummvm/translations.dat
install -m644 -D dists/pred.dic %{buildroot}%{_datadir}/scummvm/pred.dic
install -m644 -D dists/engine-data/kyra.dat %{buildroot}%{_datadir}/scummvm/kyra.dat
install -m644 -D dists/engine-data/lure.dat %{buildroot}%{_datadir}/scummvm/lure.dat
@@ -72,16 +73,31 @@ desktop-file-install --vendor scummvm --dir=%{buildroot}/%{_datadir}/application
%clean
rm -Rf ${RPM_BUILD_ROOT}
+%post
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
+%postun
+touch --no-create %{_datadir}/icons/hicolor || :
+if [ -x %{_bindir}/gtk-update-icon-cache ]; then
+ %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
+fi
+
#------------------------------------------------------------------------------
# Files listing.
#------------------------------------------------------------------------------
%files
%defattr(0644,root,root,0755)
-%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYRIGHT
+%doc AUTHORS README NEWS COPYING COPYING.LGPL COPYING.BSD COPYRIGHT
%attr(0755,root,root)%{_bindir}/scummvm
%{_datadir}/applications/*
%{_datadir}/pixmaps/scummvm.xpm
+%{_datadir}/icons/hicolor/48x48/apps/scummvm.png
+%{_datadir}/icons/hicolor/scalable/apps/scummvm.svg
%{_datadir}/scummvm/scumm*.zip
+%{_datadir}/scummvm/translations.dat
%{_datadir}/scummvm/pred.dic
%{_datadir}/scummvm/kyra.dat
%{_datadir}/scummvm/queen.tbl
@@ -95,6 +111,9 @@ rm -Rf ${RPM_BUILD_ROOT}
# Change Log
#------------------------------------------------------------------------------
%changelog
+* Fri Sep 17 2010 (1.2.0)
+ - include png/svg icons
+ - remove libmpeg2
* Thu Sep 21 2006 (0.9.1)
- include modern theme
* Mon Dec 20 2004 (0.7.0)
diff --git a/dists/redhat/scummvm48.png b/dists/redhat/scummvm48.png
new file mode 100644
index 0000000000..3f7fca230c
--- /dev/null
+++ b/dists/redhat/scummvm48.png
Binary files differ
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index f7bae3ca93..e123bebd6f 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -7,8 +7,8 @@ IDI_ICON ICON DISCARDABLE "../../icons/scummvm.ico"
#endif
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,2,0,0
- PRODUCTVERSION 1,2,0,0
+ FILEVERSION 1,3,0,0
+ PRODUCTVERSION 1,3,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -25,13 +25,13 @@ BEGIN
BEGIN
VALUE "Comments", "Look! A three headed monkey (TM)! .. Nice use of the TM!\0"
VALUE "FileDescription", "http://www.scummvm.org/\0"
- VALUE "FileVersion", "1.2.0svn\0"
+ VALUE "FileVersion", "1.3.0svn\0"
VALUE "InternalName", "scummvm\0"
VALUE "LegalCopyright", "Copyright © 2001-2010 The ScummVM Team\0"
VALUE "LegalTrademarks", "'SCUMM', and all SCUMM games are a TM of LucasArts. Simon The Sorcerer is a TM of AdventureSoft. Beneath a Steel Sky and Broken Sword are a TM of Revolution. Flight of the Amazon Queen is a TM of John Passfield and Steve Stamatiadis. \0"
VALUE "OriginalFilename", "scummvm.exe\0"
VALUE "ProductName", "ScummVM\0"
- VALUE "ProductVersion", "1.2.0svn\0"
+ VALUE "ProductVersion", "1.3.0svn\0"
END
END
BLOCK "VarFileInfo"
diff --git a/dists/slackware/scummvm.SlackBuild b/dists/slackware/scummvm.SlackBuild
index 220aa4ad08..a53c1b2a57 100755
--- a/dists/slackware/scummvm.SlackBuild
+++ b/dists/slackware/scummvm.SlackBuild
@@ -9,7 +9,7 @@ if [ "$TMP" = "" ]; then
fi
PKG=$TMP/package-scummvm
-VERSION=1.2.0svn
+VERSION=1.3.0svn
ARCH=i486
BUILD=1
diff --git a/dists/wii/meta.xml b/dists/wii/meta.xml
index 8659263076..b881b4ce39 100644
--- a/dists/wii/meta.xml
+++ b/dists/wii/meta.xml
@@ -2,7 +2,7 @@
<app version="1">
<name>ScummVM</name>
<coder>The ScummVM Team</coder>
- <version>1.2.0svn@REVISION@</version>
+ <version>1.3.0svn@REVISION@</version>
<release_date>@TIMESTAMP@</release_date>
<short_description>Point &amp; Click Adventures</short_description>
<long_description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!
diff --git a/dists/wii/meta.xml.in b/dists/wii/meta.xml.in
index 7f05bbf350..970ae7d54b 100644
--- a/dists/wii/meta.xml.in
+++ b/dists/wii/meta.xml.in
@@ -8,5 +8,6 @@
<long_description>ScummVM is a program which allows you to run certain classic graphical point-and-click adventure games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed!
Some of the adventures ScummVM supports include Adventure Soft's Simon the Sorcerer 1 and 2; Revolution's Beneath A Steel Sky and Broken Sword I &amp; II; Flight of the Amazon Queen; Wyrmkeep's Inherit the Earth; Coktel Vision's Gobliiins; Westwood Studios' The Legend of Kyrandia and games based on LucasArts' SCUMM (Script Creation Utility for Maniac Mansion) system such as Monkey Island, Day of the Tentacle, Sam and Max and more.</long_description>
+ <no_ios_reload/>
</app>
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 46ecc03767..d31330b69e 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -226,7 +226,7 @@ bool cleanupPirated(ADGameDescList &matched) {
// 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;
@@ -400,7 +400,7 @@ static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles,
matched = true;
break;
}
-
+
if (!matched)
continue;
@@ -448,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;
@@ -472,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;
}
@@ -622,7 +622,7 @@ static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParam
matchedDesc = agdesc;
maxNumMatchedFiles = numMatchedFiles;
- debug(4, "and overriden");
+ debug(4, "and overridden");
}
}
}
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index b15a4c3bac..4dcf5fee5e 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -360,12 +360,12 @@ int AgiEngine::agiInit() {
switch (getVersion() >> 12) {
case 2:
- report("Emulating Sierra AGI v%x.%03x\n",
+ debug("Emulating Sierra AGI v%x.%03x\n",
(int)(getVersion() >> 12) & 0xF,
(int)(getVersion()) & 0xFFF);
break;
case 3:
- report("Emulating Sierra AGI v%x.002.%03x\n",
+ debug("Emulating Sierra AGI v%x.002.%03x\n",
(int)(getVersion() >> 12) & 0xF,
(int)(getVersion()) & 0xFFF);
break;
@@ -382,10 +382,10 @@ int AgiEngine::agiInit() {
_game.sbuf = _game.sbuf256c;
if (_game.gameFlags & ID_AMIGA)
- report("Amiga padded game detected.\n");
+ debug(1, "Amiga padded game detected.");
if (_game.gameFlags & ID_AGDS)
- report("AGDS mode enabled.\n");
+ debug(1, "AGDS mode enabled.");
ec = _loader->init(); // load vol files, etc
@@ -577,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;
}
@@ -648,7 +655,7 @@ void AgiEngine::initialize() {
_game.state = STATE_LOADED;
debugC(2, kDebugLevelMain, "game loaded");
} else {
- report("Could not open AGI game");
+ warning("Could not open AGI game");
}
debugC(2, kDebugLevelMain, "Init sound");
@@ -696,7 +703,6 @@ Common::Error AgiBase::init() {
Common::Error AgiEngine::go() {
CursorMan.showMouse(true);
- report(" \nAGI engine %s is ready.\n", gScummVMVersion);
if (_game.state < STATE_LOADED) {
do {
mainCycle();
@@ -709,6 +715,33 @@ Common::Error AgiEngine::go() {
}
void AgiEngine::parseFeatures() {
+
+ /* FIXME: Seems this method doesn't really do anything. It might
+ be a leftover that could be removed, except that some of its
+ intended purpose may still need to be reimplemented.
+
+ [0:29] <Fingolfin> can you tell me what the point behind AgiEngine::parseFeatures() is?
+ [0:30] <_sev> when games are created with WAGI studio
+ [0:31] <_sev> it creates .wag site with game-specific features such as full game title, whether to use AGIMOUSE etc
+ [0:32] <Fingolfin> ... and the "features" config key is created by our detector based on the wag file, I guess?
+ [0:33] <_sev> yes
+ [0:33] <Fingolfin> it's just that I cant seem to find a place we do that
+ [0:33] <_sev> it is used for fallback
+ [0:34] <_sev> ah, perhaps it was not updated
+ [0:34] <Fingolfin> I only see us check the value, but never set it
+ [0:34] <Fingolfin> maybe I am grepping wrong, who knows :)
+ [0:44] <Fingolfin> _sev: so, unless I miss something, it seem that function does nothing right now
+ [0:45] <_sev> Fingolfin: it could be unfinished. It was part of GSoC 3 years ago
+ [0:45] <Fingolfin> well
+ [0:45] <_sev> I just don't remember
+ [0:45] <Fingolfin> but don't we just re-parse the wag when the game is loaded anyway?
+ [0:45] <_sev> but it documents the format
+ [0:45] <Fingolfin> the advanced meta engine would re-run the detector, wouldn't it?
+ [0:45] <_sev> yep
+ [0:47] <Fingolfin> so... shouldn't we at least add a comment to the function explaining what it does and that it's unfinished etc.? maybe add a TODO to the wiki?
+ [0:47] <Fingolfin> otherwise it might stay as it is for another 3 years :)
+ */
+
if (!ConfMan.hasKey("features"))
return;
@@ -740,13 +773,15 @@ void AgiEngine::parseFeatures() {
for (int i = 0; i < numFeatures; i++) {
for (const Flags *flag = flags; flag->name; flag++) {
if (!scumm_stricmp(feature[i], flag->name)) {
- debug(0, "Added feature: %s", flag->name);
+ debug(2, "Added feature: %s", flag->name);
setFeature(flag->flag);
break;
}
}
}
+
+ free(features);
}
} // End of namespace Agi
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 4df8824b0e..8ff7f6c35e 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -52,8 +52,12 @@ namespace Common { class RandomSource; }
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Early Sierra adventure games
+ * - many fan made games
+ * - Mickey's Space Adventure (Pre-AGI)
+ * - Winnie the Pooh in the Hundred Acre Wood (Pre-AGI)
+ * - Troll's Tale (Pre-AGI)
*/
namespace Agi {
@@ -232,8 +236,6 @@ enum AgiMouseButton {
kAgiMouseButtonMiddle // Middle mouse button
};
-#define report printf
-
enum GameId {
GID_AGI = 1
};
@@ -556,8 +558,8 @@ struct AgiGame {
int lineUserInput; /**< line to put user input on */
int lineMinPrint; /**< num lines to print on */
int cursorPos; /**< column where the input cursor is */
- uint8 inputBuffer[40]; /**< buffer for user input */
- uint8 echoBuffer[40]; /**< buffer for echo.line */
+ byte inputBuffer[40]; /**< buffer for user input */
+ byte echoBuffer[40]; /**< buffer for echo.line */
int keypress;
InputMode inputMode; /**< keyboard input mode */
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
index e5942455e2..370f48e018 100644
--- a/engines/agi/console.cpp
+++ b/engines/agi/console.cpp
@@ -152,7 +152,7 @@ bool Console::Cmd_Flags(int argc, const char **argv) {
for (j = 0; j < 10; j++, i++) {
DebugPrintf("%c ", _vm->getflag(i) ? 'T' : 'F');
}
- report("\n");
+ DebugPrintf("\n");
}
return true;
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 57bcabb188..86c1b519a9 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -319,12 +319,12 @@ int AgiEngine::playGame() {
// We run AGIMOUSE always as a side effect
if (getFeatures() & GF_AGIMOUSE || true)
- report("Using AGI Mouse 1.0 protocol\n");
+ debug(1, "Using AGI Mouse 1.0 protocol");
if (getFeatures() & GF_AGIPAL)
debug(1, "Running AGIPAL game");
- report("Running AGI script.\n");
+ debug(0, "Running AGI script.\n");
setflag(fEnteredCli, false);
setflag(fSaidAcceptedInput, 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/id.cpp b/engines/agi/id.cpp
index 77c527444b..2e12e45458 100644
--- a/engines/agi/id.cpp
+++ b/engines/agi/id.cpp
@@ -47,7 +47,7 @@ int AgiEngine::setupV2Game(int ver) {
if (getFeatures() & GF_AGDS)
setVersion(ver = 0x2440); // ALL AGDS games built for 2.440
- report("Setting up for version 0x%04X\n", ver);
+ debug(0, "Setting up for version 0x%04X", ver);
// 'quit' takes 0 args for 2.089
if (ver == 0x2089)
@@ -70,7 +70,7 @@ int AgiEngine::setupV2Game(int ver) {
int AgiEngine::setupV3Game(int ver) {
int ec = errOK;
- report("Setting up for version 0x%04X\n", ver);
+ debug(0, "Setting up for version 0x%04X", ver);
// 'unknown176' takes 1 arg for 3.002.086, not 0 args.
// 'unknown173' also takes 1 arg for 3.002.068, not 0 args.
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/keyboard.cpp b/engines/agi/keyboard.cpp
index 62bcd5d8d8..344654128d 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -121,7 +121,6 @@ int AgiEngine::handleController(int key) {
if (_game.controllers[i].keycode == key) {
debugC(3, kDebugLevelInput, "event %d: key press", _game.controllers[i].controller);
_game.controllerOccured[_game.controllers[i].controller] = true;
- report("event AC:%i occurred\n", _game.controllers[i].controller);
return true;
}
}
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index de6f8d0653..54b40d66b2 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -43,7 +43,7 @@ int AgiLoader_v2::loadDir(AgiDir *agid, const char *fname) {
uint32 flen;
uint i;
- report("Loading directory: %s\n", fname);
+ debug(0, "Loading directory: %s", fname);
if (!fp.open(fname)) {
return errBadFileOpen;
@@ -157,7 +157,7 @@ uint8 *AgiLoader_v2::loadVolRes(struct AgiDir *agid) {
exit(1);
}
} else {
- report("Error: bad signature %04x\n", sig);
+ warning("AgiLoader_v2::loadVolRes: bad signature %04x", sig);
// fprintf (stderr, "ACK! BAD RESOURCE!!!\n");
return 0;
}
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/objects.cpp b/engines/agi/objects.cpp
index 5d1866bea4..8de36f24e1 100644
--- a/engines/agi/objects.cpp
+++ b/engines/agi/objects.cpp
@@ -46,9 +46,9 @@ int AgiEngine::decodeObjects(uint8 *mem, uint32 flen) {
// if so, its encrypted, else it is not
if (READ_LE_UINT16(mem) > flen) {
- report("Decrypting objects... ");
+ debugN(0, "Decrypting objects... ");
decrypt(mem, flen);
- report("done.\n");
+ debug(0, "done.");
}
// alloc memory for object list
@@ -79,7 +79,7 @@ int AgiEngine::decodeObjects(uint8 *mem, uint32 flen) {
(_objects + i)->name = strdup("");
}
}
- report("Reading objects: %d objects read.\n", _game.numObjects);
+ debug(0, "Reading objects: %d objects read.", _game.numObjects);
return errOK;
}
@@ -92,8 +92,7 @@ int AgiEngine::loadObjects(const char *fname) {
_objects = NULL;
_game.numObjects = 0;
- debugC(5, kDebugLevelResources, "(fname = %s)", fname);
- report("Loading objects: %s\n", fname);
+ debugC(5, kDebugLevelResources, "(Loading objects '%s')", fname);
if (!fp.open(fname))
return errBadFileOpen;
@@ -130,7 +129,7 @@ void AgiEngine::unloadObjects() {
void AgiEngine::objectSetLocation(unsigned int n, int i) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectSetLocation: Can't access object %d.\n", n);
return;
}
_objects[n].location = i;
@@ -138,7 +137,7 @@ void AgiEngine::objectSetLocation(unsigned int n, int i) {
int AgiEngine::objectGetLocation(unsigned int n) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectGetLocation: Can't access object %d.\n", n);
return 0;
}
return _objects[n].location;
@@ -146,7 +145,7 @@ int AgiEngine::objectGetLocation(unsigned int n) {
const char *AgiEngine::objectName(unsigned int n) {
if (n >= _game.numObjects) {
- report("Error: Can't access object %d.\n", n);
+ warning("AgiEngine::objectName: Can't access object %d.\n", n);
return "";
}
return _objects[n].name;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index a60080186c..2ea53e57ad 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -492,11 +492,12 @@ void AgiEngine::cmd_init_joy(uint8 *p) { // do nothing
}
void AgiEngine::cmd_script_size(uint8 *p) {
- report("script.size(%d)\n", p0);
+ debug(0, "script.size(%d)", p0);
}
void AgiEngine::cmd_cancel_line(uint8 *p) {
_game.inputBuffer[0] = 0;
+ _game.cursorPos = 0;
writePrompt();
}
@@ -608,7 +609,7 @@ void AgiEngine::cmd_set_simple(uint8 *p) {
void AgiEngine::cmd_pop_script(uint8 *p) {
if (getVersion() >= 0x2915) {
- report("pop.script\n");
+ debug(0, "pop.script");
}
}
@@ -620,7 +621,7 @@ void AgiEngine::cmd_hold_key(uint8 *p) {
void AgiEngine::cmd_discard_sound(uint8 *p) {
if (getVersion() >= 0x2936) {
- report("discard.sound\n");
+ debug(0, "discard.sound");
}
}
@@ -1100,7 +1101,7 @@ void AgiEngine::cmd_set_game_id(uint8 *p) {
else
_game.id[0] = 0;
- report("Game ID: \"%s\"\n", _game.id);
+ debug(0, "Game ID: \"%s\"", _game.id);
}
void AgiEngine::cmd_pause(uint8 *p) {
@@ -1452,7 +1453,7 @@ void AgiEngine::cmd_clear_text_rect(uint8 *p) {
}
void AgiEngine::cmd_toggle_monitor(uint8 *p) {
- report("toggle.monitor\n");
+ debug(0, "toggle.monitor");
}
void AgiEngine::cmd_echo_line(uint8 *p) {
@@ -1509,7 +1510,7 @@ void AgiEngine::cmd_push_script(uint8 *p) {
_game.vars[29] = _mouse.y;
} else {
if (getVersion() >= 0x2915) {
- report("push.script\n");
+ debug(0, "push.script");
}
}
}
@@ -1517,7 +1518,7 @@ void AgiEngine::cmd_push_script(uint8 *p) {
void AgiEngine::cmd_set_pri_base(uint8 *p) {
int i, x, pri;
- report("Priority base set to %d\n", p0);
+ debug(0, "Priority base set to %d", p0);
// _game.alt_pri = true;
x = (_HEIGHT - p0) * _HEIGHT / 10;
diff --git a/engines/agi/op_dbg.cpp b/engines/agi/op_dbg.cpp
index be10f6ee75..f3b4815790 100644
--- a/engines/agi/op_dbg.cpp
+++ b/engines/agi/op_dbg.cpp
@@ -275,11 +275,11 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
uint8 a, c, z;
if (str) {
- report(" %s\n", str);
+ debug(0, " %s", str);
return;
}
- report("%03d:%04x ", lognum, ip);
+ debugN(0, "%03d:%04x ", lognum, ip);
switch (*(code + ip)) {
case 0xFC:
@@ -289,7 +289,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
x = logicNamesIf;
if (_debug.opcodes) {
- report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ debugN(0, "%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8)*(code + (0 + ip)) & 0xFF,
(uint8)*(code + (1 + ip)) & 0xFF,
@@ -301,7 +301,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
(uint8)*(code + (7 + ip)) & 0xFF,
(uint8)*(code + (8 + ip)) & 0xFF);
}
- report("%s ", (x + *(code + ip) - 0xFC)->name);
+ debugN(0, "%s ", (x + *(code + ip) - 0xFC)->name);
break;
default:
x = mode == lCOMMAND_MODE ? logicNamesCmd : logicNamesTest;
@@ -309,7 +309,7 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
c = (unsigned char)(x + *(code + ip))->argMask;
if (_debug.opcodes) {
- report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ debugN(0, "%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
" ",
(uint8)*(code + (0 + ip)) & 0xFF,
(uint8)*(code + (1 + ip)) & 0xFF,
@@ -321,23 +321,23 @@ void AgiEngine::debugConsole(int lognum, int mode, const char *str) {
(uint8)*(code + (7 + ip)) & 0xFF,
(uint8)*(code + (8 + ip)) & 0xFF);
}
- report("%s ", (x + *(code + ip))->name);
+ debugN(0, "%s ", (x + *(code + ip))->name);
for (z = 1; a > 0;) {
if (~c & 0x80) {
- report("%d", *(code + (ip + z)));
+ debugN(0, "%d", *(code + (ip + z)));
} else {
- report("v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
+ debugN(0, "v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
}
c <<= 1;
z++;
if (--a > 0)
- report(",");
+ debugN(0, ",");
}
break;
}
- report("\n");
+ debugN(0, "\n");
}
} // End of namespace Agi
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/preagi_mickey.cpp b/engines/agi/preagi_mickey.cpp
index ec8a1f4878..ea041a93c7 100644
--- a/engines/agi/preagi_mickey.cpp
+++ b/engines/agi/preagi_mickey.cpp
@@ -302,6 +302,8 @@ void Mickey::getMouseMenuSelRow(MSA_MENU menu, int *sel0, int *sel1, int iRow, i
if (y != IDI_MSA_ROW_MENU_1) return;
sel = sel1;
break;
+ default:
+ return;
}
for (iWord = 0; iWord < menu.row[iRow].count; iWord++) {
@@ -1234,7 +1236,7 @@ int Mickey::getPlanet() {
if (!_game.nButtons)
return -1;
- for (int iPlanet = 0; iPlanet < IDI_MSA_MAX_DAT; iPlanet++) {
+ for (int iPlanet = 0; iPlanet < IDI_MSA_MAX_DAT - 1; iPlanet++) {
if (!strcmp(IDS_MSA_ADDR_PLANET[iPlanet], _game.szAddr)) {
return iPlanet;
}
@@ -1313,7 +1315,7 @@ void Mickey::flipSwitch() {
_game.iPlanetXtal[0] = IDI_MSA_PLANET_EARTH;
_game.iPlanetXtal[8] = IDI_MSA_PLANET_URANUS;
- for (int i = 1; i < 9; i++) {
+ for (int i = 1; i < IDI_MSA_MAX_PLANET; i++) {
if (i < 8) {
do {
// Earth (planet 0) and Uranus (planet 8) are excluded
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_coco3.cpp b/engines/agi/sound_coco3.cpp
index f054be0682..858c1c8515 100644
--- a/engines/agi/sound_coco3.cpp
+++ b/engines/agi/sound_coco3.cpp
@@ -61,9 +61,9 @@ void SoundGenCoCo3::play(int resnum) {
uint32 start_time = _vm->_system->getMillis();
while (_vm->_system->getMillis() < start_time + note.duration) {
- _vm->_system->updateScreen();
+ _vm->_system->updateScreen();
- _vm->_system->delayMillis(10);
+ _vm->_system->delayMillis(10);
}
}
} while (note.freq != 0xff);
diff --git a/engines/agi/sound_midi.cpp b/engines/agi/sound_midi.cpp
index 840538b92b..f7f38a8d55 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();
@@ -122,10 +129,10 @@ int SoundGenMIDI::open() {
_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;
}
@@ -287,7 +294,7 @@ static uint32 convertSND2MIDI(byte *snddata, byte **data) {
for (n = 0; n < 3; n++) {
uint16 start, end, pos;
-
+
st.write("MTrk", 4);
lp = st.pos();
st.writeUint32BE(0); /* chunklength */
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/agi/sound_sarien.cpp b/engines/agi/sound_sarien.cpp
index 08bdd47497..4ede50a749 100644
--- a/engines/agi/sound_sarien.cpp
+++ b/engines/agi/sound_sarien.cpp
@@ -95,13 +95,10 @@ SoundGenSarien::SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(v
break;
}
- report("Initializing sound:\n");
-
- report("sound: envelopes ");
if (_env) {
- report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
+ debug(0, "Initializing sound: envelopes enabled (decay=%d, sustain=%d)", ENV_DECAY, ENV_SUSTAIN);
} else {
- report("disabled\n");
+ debug(0, "Initializing sound: envelopes disabled");
}
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 464c218ae8..c48ed90ad8 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -52,10 +52,10 @@ int AgiEngine::loadWords(const char *fname) {
words = NULL;
if (!fp.open(fname)) {
- report("Warning: can't open %s\n", fname);
+ warning("loadWords: can't open %s", fname);
return errOK; // err_BadFileOpen
}
- report("Loading dictionary: %s\n", fname);
+ debug(0, "Loading dictionary: %s", fname);
fp.seek(0, SEEK_END);
flen = fp.pos();
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 670c701198..10b6416c61 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -562,11 +562,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);
@@ -926,10 +926,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/agos.h b/engines/agos/agos.h
index b12bf09d62..5ebfc7b87f 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -47,8 +47,13 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Elvira: Mistress of the Dark
+ * - Elvira 2: The Jaws of Cerberus
+ * - The Feeble Files
+ * - Simon the Sorcerer
+ * - Simon the Sorcerer 2
+ * - Simon the Sorcerer Puzzle Pack
*/
namespace AGOS {
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/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/script_pn.cpp b/engines/agos/script_pn.cpp
index 0391d67b31..909c051362 100644
--- a/engines/agos/script_pn.cpp
+++ b/engines/agos/script_pn.cpp
@@ -890,7 +890,7 @@ int AGOSEngine_PN::doline(int needsave) {
int myTag = ++_tagOfActiveDoline; // Obtain a unique tag for this doline invocation
_dolineReturnVal = 0;
- if (needsave)
+ if (_stackbase && needsave)
_stackbase->tagOfParentDoline = myTag;
do {
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/agos/verb_pn.cpp b/engines/agos/verb_pn.cpp
index 129e1dec0e..b36f634ec0 100644
--- a/engines/agos/verb_pn.cpp
+++ b/engines/agos/verb_pn.cpp
@@ -185,7 +185,7 @@ void AGOSEngine_PN::hitBox5(HitArea *ha) {
_mousePrintFG++;
_mouseString = (const char *)"take \0";
- _mouseString1 = getMessage(_objectName1, _dragStore->msg1);
+ _mouseString1 = _dragStore ? getMessage(_objectName1, _dragStore->msg1) : "";
if (_dragStore->flags & kOBFRoomBox)
_mouseString1 = (const char *)"all\r";
diff --git a/engines/cine/cine.h b/engines/cine/cine.h
index 114d98d442..cab6b92a5d 100644
--- a/engines/cine/cine.h
+++ b/engines/cine/cine.h
@@ -71,7 +71,7 @@
* yet been finished. The game is not completable.
*
*
- * Supported games:
+ * Games using this engine:
*
* Cinematique evo.1
* - Future Wars
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/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index 430a32ac69..5db9a83928 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -3098,7 +3098,7 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx)
}
void dumpScript(char *dumpName) {
- Common::DumpFile fHandle;
+ Common::DumpFile fHandle;
uint16 i;
fHandle.open(dumpName);
diff --git a/engines/cruise/background.cpp b/engines/cruise/background.cpp
index 3ac57cc376..edd52d3b4a 100644
--- a/engines/cruise/background.cpp
+++ b/engines/cruise/background.cpp
@@ -216,8 +216,7 @@ int loadBackground(const char *name, int idx) {
if (strlen(name) >= sizeof(backgroundTable[idx].name))
warning("background name length exceeded allowable maximum");
- strncpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
- backgroundTable[idx].name[sizeof(backgroundTable[idx].name) - 1] = 0;
+ Common::strlcpy(backgroundTable[idx].name, name, sizeof(backgroundTable[idx].name));
}
return (0);
diff --git a/engines/cruise/cruise.h b/engines/cruise/cruise.h
index 94f5c68ca0..ad3bb20ca1 100644
--- a/engines/cruise/cruise.h
+++ b/engines/cruise/cruise.h
@@ -42,7 +42,7 @@
*
* Status of this engine: Game is completable, engine needs objectifying
*
- * Supported games:
+ * Games using this engine:
* - Cruise for a Corpse
*/
namespace Cruise {
diff --git a/engines/cruise/dataLoader.cpp b/engines/cruise/dataLoader.cpp
index b2ac7a2fd6..b48240b30e 100644
--- a/engines/cruise/dataLoader.cpp
+++ b/engines/cruise/dataLoader.cpp
@@ -55,7 +55,7 @@ void decodeGfxUnified(dataFileEntry *pCurrentFileEntry, int16 format) {
break;
default:
- error("Unkown gfx format %d", format);
+ error("Unknown gfx format %d", format);
}
uint8 *buffer = (uint8 *)MemAlloc(spriteSize);
diff --git a/engines/cruise/function.cpp b/engines/cruise/function.cpp
index 3d07abf441..c3d435bde6 100644
--- a/engines/cruise/function.cpp
+++ b/engines/cruise/function.cpp
@@ -58,9 +58,9 @@ int16 Op_LoadOverlay() {
updateAllScriptsImports();
- strcpy(nextOverlay, overlayName);
+ Common::strlcpy(nextOverlay, overlayName, sizeof(nextOverlay));
- return(overlayLoadResult);
+ return overlayLoadResult;
}
int16 Op_Strcpy() {
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/cruise/script.cpp b/engines/cruise/script.cpp
index d6c1aa47f3..3d07575c16 100644
--- a/engines/cruise/script.cpp
+++ b/engines/cruise/script.cpp
@@ -185,8 +185,9 @@ int32 opcodeType1() {
return 0;
}
case 2: {
+ assert (ptr);
*(ptr + var_A + offset) = var;
- return (0);
+ return 0;
}
default:
error("Unsupported code in opcodeType1 case 1");
diff --git a/engines/cruise/vars.cpp b/engines/cruise/vars.cpp
index c61cedc503..07bd646cae 100644
--- a/engines/cruise/vars.cpp
+++ b/engines/cruise/vars.cpp
@@ -51,8 +51,8 @@ int32 volumeDataLoaded = 0;
int16 numOfDisks;
-char lastOverlay[15];
-char nextOverlay[15];
+char lastOverlay[38];
+char nextOverlay[38];
int16 currentActiveMenu;
int16 autoMsg;
diff --git a/engines/cruise/vars.h b/engines/cruise/vars.h
index af39993f2f..54920a1436 100644
--- a/engines/cruise/vars.h
+++ b/engines/cruise/vars.h
@@ -154,8 +154,8 @@ extern int32 volumeDataLoaded;
extern int16 numOfDisks;
-extern char lastOverlay[15];
-extern char nextOverlay[15];
+extern char lastOverlay[38];
+extern char nextOverlay[38];
extern int16 currentActiveMenu;
extern int16 autoMsg;
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index cb081e4683..6b0f4d3b39 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -36,6 +36,7 @@
#include "gui/GuiManager.h"
#include "gui/launcher.h"
#include "gui/ListWidget.h"
+#include "gui/message.h"
#include "gui/options.h"
#include "gui/saveload.h"
#include "gui/ThemeEval.h"
@@ -102,7 +103,6 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
// To enable "Help", an engine needs to use a subclass of MainMenuDialog
// (at least for now, we might change how this works in the future).
_helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", _("~H~elp"), 0, kHelpCmd);
- _helpButton->setEnabled(false);
new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd);
@@ -147,8 +147,13 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
case kAboutCmd:
_aboutDialog->runModal();
break;
- case kHelpCmd:
- // Not handled here -- needs to be handled by a subclass (for now)
+ case kHelpCmd: {
+ GUI::MessageDialog dialog(
+ "Sorry, this engine does not currently provide in-game help. "
+ "Please consult the README for basic information, and for "
+ "instructions on how to obtain further assistance.");
+ dialog.runModal();
+ }
break;
case kRTLCmd: {
Common::Event eventRTL;
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index 07a9928cfa..ce426740fa 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -202,10 +202,7 @@ SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int
int minutes = header.time & 0xFF;
desc.setSaveTime(hour, minutes);
- minutes = header.playtime / 60;
- hour = minutes / 60;
- minutes %= 60;
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(header.playtime * 1000);
return desc;
}
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index cd3920b30d..7a7d7b8372 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
@@ -402,7 +403,7 @@ DraciEngine::~DraciEngine() {
Common::Error DraciEngine::run() {
init();
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime(0);
_game->init();
// Load game from specified slot, if any
@@ -417,8 +418,6 @@ Common::Error DraciEngine::run() {
void DraciEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
if (pause) {
- // Record start of the pause, so that we can later
- // adjust _engineStartTime accordingly.
_pauseStartTime = _system->getMillis();
_anims->pauseAnimations();
@@ -433,7 +432,6 @@ void DraciEngine::pauseEngineIntern(bool pause) {
// Adjust engine start time
const int delta = _system->getMillis() - _pauseStartTime;
- _engineStartTime += delta / 1000;
_game->shiftSpeechAndFadeTick(delta);
_pauseStartTime = 0;
}
diff --git a/engines/draci/draci.h b/engines/draci/draci.h
index 605e8cc238..f2cc2ce2e7 100644
--- a/engines/draci/draci.h
+++ b/engines/draci/draci.h
@@ -37,10 +37,10 @@ class OSystem;
/**
* This is the namespace of the Draci engine.
*
- * Status of this engine: ???
+ * Status of this engine: Complete
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Dragon History
*/
namespace Draci {
@@ -107,7 +107,6 @@ public:
Common::RandomSource _rnd;
- int32 _engineStartTime;
int32 _pauseStartTime;
};
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 8f3ad12cfd..cb2832552a 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -207,7 +207,7 @@ void Game::init() {
_currentItem = _itemUnderCursor = NULL;
_previousItemPosition = -1;
-
+
_vm->_mouse->setCursorType(kHighlightedCursor); // anything different from kNormalCursor
_objUnderCursor = NULL;
@@ -1618,15 +1618,15 @@ int GameObject::addAnim(Animation *anim) {
}
void GameObject::playAnim(int i) {
- _anim[i]->play();
- _playingAnim = i;
+ _anim[i]->play();
+ _playingAnim = i;
}
void GameObject::stopAnim() {
- if (_playingAnim >= 0) {
- _anim[_playingAnim]->stop();
- _playingAnim = -1;
- }
+ if (_playingAnim >= 0) {
+ _anim[_playingAnim]->stop();
+ _playingAnim = -1;
+ }
}
void GameObject::deleteAnims() {
diff --git a/engines/draci/mouse.cpp b/engines/draci/mouse.cpp
index 1d251d24c3..14d1162fed 100644
--- a/engines/draci/mouse.cpp
+++ b/engines/draci/mouse.cpp
@@ -54,10 +54,10 @@ void Mouse::handleEvent(Common::Event event) {
case Common::EVENT_LBUTTONUP:
debugC(6, kDraciGeneralDebugLevel, "Left button up (x: %u y: %u)", _x, _y);
- // Don't set _lButton to false, because some touchpads generate
- // down and up at such a quick succession, that they will
- // cancel each other in the same call of handleEvents(). Let
- // the game clear this flag by calling lButtonSet() instead.
+ // Don't set _lButton to false, because some touchpads generate
+ // down and up at such a quick succession, that they will
+ // cancel each other in the same call of handleEvents(). Let
+ // the game clear this flag by calling lButtonSet() instead.
break;
case Common::EVENT_RBUTTONDOWN:
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/saveload.cpp b/engines/draci/saveload.cpp
index 856e6da832..32e852d9a6 100644
--- a/engines/draci/saveload.cpp
+++ b/engines/draci/saveload.cpp
@@ -103,7 +103,7 @@ Common::Error saveSavegameData(int saveGameIdx, const Common::String &saveName,
header.saveName = saveName;
header.date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
header.time = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
- header.playtime = vm._system->getMillis() / 1000 - vm._engineStartTime;
+ header.playtime = vm.getTotalPlayTime() / 1000;
writeSavegameHeader(f, header);
if (f->err()) {
@@ -157,7 +157,7 @@ Common::Error loadSavegameData(int saveGameIdx, DraciEngine *vm) {
vm->_game->inventoryReload();
- vm->_engineStartTime = vm->_system->getMillis() / 1000 - header.playtime;
+ vm->setTotalPlayTime(header.playtime * 1000);
return Common::kNoError;
}
diff --git a/engines/draci/sound.cpp b/engines/draci/sound.cpp
index c9244d7eac..6828066c3a 100644
--- a/engines/draci/sound.cpp
+++ b/engines/draci/sound.cpp
@@ -407,14 +407,14 @@ void Sound::stopVoice() {
}
void Sound::setVolume() {
- if (_mixer->isReady()) {
- _muteSound = ConfMan.getBool("sfx_mute");
- _muteVoice = ConfMan.getBool("speech_mute");
- } else {
- _muteSound = _muteVoice = true;
- }
+ if (_mixer->isReady()) {
+ _muteSound = ConfMan.getBool("sfx_mute");
+ _muteVoice = ConfMan.getBool("speech_mute");
+ } else {
+ _muteSound = _muteVoice = true;
+ }
if (ConfMan.getBool("mute")) {
- _muteSound = _muteVoice = true;
+ _muteSound = _muteVoice = true;
}
_showSubtitles = ConfMan.getBool("subtitles");
_talkSpeed = ConfMan.getInt("talkspeed");
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/drascula.h b/engines/drascula/drascula.h
index 0a8b7c8c9b..535a9005d2 100644
--- a/engines/drascula/drascula.h
+++ b/engines/drascula/drascula.h
@@ -47,8 +47,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Drascula: The Vampire Strikes Back
*/
namespace Drascula {
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/resource.cpp b/engines/drascula/resource.cpp
index 1226bc2e10..57be51da43 100644
--- a/engines/drascula/resource.cpp
+++ b/engines/drascula/resource.cpp
@@ -92,13 +92,19 @@ void TextResourceParser::getLine(char *buf) {
void TextResourceParser::parseInt(int &result) {
char buf[256];
getLine(buf);
- sscanf(buf, "%d", &result);
+
+ if (!sscanf(buf, "%d", &result)) {
+ result = 0;
+ }
}
void TextResourceParser::parseString(char* result) {
char buf[256];
getLine(buf);
- sscanf(buf, "%s", result);
+
+ if (!sscanf(buf, "%s", result)) {
+ *result = 0;
+ }
}
diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp
index 57bfad26af..c6dd9f29db 100644
--- a/engines/drascula/rooms.cpp
+++ b/engines/drascula/rooms.cpp
@@ -1945,7 +1945,9 @@ bool DrasculaEngine::exitRoom(int doorNumber) {
hare_se_ve = 1;
clearRoom();
- sscanf(_targetSurface[doorNumber], "%d", &roomNum);
+ if (!sscanf(_targetSurface[doorNumber], "%d", &roomNum)) {
+ error("Malformed roomNum in targetSurface (%s)", _targetSurface[doorNumber]);
+ }
curX = -1;
enterRoom(roomNum);
diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp
index abf17d0e8e..4c288553a2 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();
}
@@ -221,7 +221,9 @@ bool DrasculaEngine::loadGame(const char *gameName) {
takeObject = sav->readSint32LE();
pickedObject = sav->readSint32LE();
loadedDifferentChapter = 0;
- sscanf(currentData, "%d.ald", &roomNum);
+ if (!sscanf(currentData, "%d.ald", &roomNum)) {
+ error("Bad save format");
+ }
enterRoom(roomNum);
selectVerb(kVerbNone);
@@ -241,7 +243,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 e2c0bb79f3..0af01f72c9 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -94,6 +94,8 @@ Engine::Engine(OSystem *syst)
_saveFileMan(_system->getSavefileManager()),
_targetName(ConfMan.getActiveDomainName()),
_pauseLevel(0),
+ _pauseStartTime(0),
+ _engineStartTime(_system->getMillis()),
_mainMenuDialog(NULL) {
g_engine = this;
@@ -110,7 +112,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.
@@ -380,9 +382,12 @@ void Engine::pauseEngine(bool pause) {
_pauseLevel--;
if (_pauseLevel == 1 && pause) {
+ _pauseStartTime = _system->getMillis();
pauseEngineIntern(true);
} else if (_pauseLevel == 0) {
pauseEngineIntern(false);
+ _engineStartTime += _system->getMillis() - _pauseStartTime;
+ _pauseStartTime = 0;
}
}
@@ -398,6 +403,24 @@ void Engine::openMainMenuDialog() {
syncSoundSettings();
}
+uint32 Engine::getTotalPlayTime() const {
+ if (!_pauseLevel)
+ return _system->getMillis() - _engineStartTime;
+ else
+ return _pauseStartTime - _engineStartTime;
+}
+
+void Engine::setTotalPlayTime(uint32 time) {
+ const uint32 currentTime = _system->getMillis();
+
+ // We need to reset the pause start time here in case the engine is already
+ // paused to avoid any incorrect play time counting.
+ if (_pauseLevel > 0)
+ _pauseStartTime = currentTime;
+
+ _engineStartTime = currentTime - time;
+}
+
int Engine::runDialog(GUI::Dialog &dialog) {
pauseEngine(true);
int result = dialog.runModal();
diff --git a/engines/engine.h b/engines/engine.h
index ead1526d72..b4764319b8 100644
--- a/engines/engine.h
+++ b/engines/engine.h
@@ -74,6 +74,17 @@ private:
*/
int _pauseLevel;
+ /**
+ * The time when the pause was started.
+ */
+ uint32 _pauseStartTime;
+
+ /**
+ * The time when the engine was started. This value is used to calculate
+ * the current play time of the game running.
+ */
+ int32 _engineStartTime;
+
public:
@@ -234,6 +245,24 @@ public:
*/
void openMainMenuDialog();
+ /**
+ * Get the total play time.
+ *
+ * @return How long the player has been playing in ms.
+ */
+ uint32 getTotalPlayTime() const;
+
+ /**
+ * Set the game time counter to the specified time.
+ *
+ * This can be used to set the play time counter after loading a savegame
+ * for example. Another use case is in case the engine wants to exclude
+ * time from the counter the user spent in original engine dialogs.
+ *
+ * @param time Play time to set up in ms.
+ */
+ void setTotalPlayTime(uint32 time = 0);
+
inline Common::TimerManager *getTimerManager() { return _timer; }
inline Common::EventManager *getEventManager() { return _eventMan; }
inline Common::SaveFileManager *getSaveFileManager() { return _saveFileMan; }
diff --git a/engines/engines.mk b/engines/engines.mk
index e542ffd933..eea4ffc0b9 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -74,6 +74,11 @@ DEFINES += -DENABLE_LOL
endif
endif
+ifdef ENABLE_LASTEXPRESS
+DEFINES += -DENABLE_LASTEXPRESS=$(ENABLE_LASTEXPRESS)
+MODULES += engines/lastexpress
+endif
+
ifdef ENABLE_LURE
DEFINES += -DENABLE_LURE=$(ENABLE_LURE)
MODULES += engines/lure
@@ -141,6 +146,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
@@ -151,6 +166,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/console.cpp b/engines/gob/console.cpp
new file mode 100644
index 0000000000..247911402f
--- /dev/null
+++ b/engines/gob/console.cpp
@@ -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$
+ *
+ */
+
+#include "gob/console.h"
+#include "gob/gob.h"
+#include "gob/inter.h"
+
+namespace Gob {
+
+GobConsole::GobConsole(GobEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("varSize", WRAP_METHOD(GobConsole, cmd_varSize));
+ DCmd_Register("var8", WRAP_METHOD(GobConsole, cmd_var8));
+ DCmd_Register("var16", WRAP_METHOD(GobConsole, cmd_var16));
+ DCmd_Register("var32", WRAP_METHOD(GobConsole, cmd_var32));
+ DCmd_Register("varString", WRAP_METHOD(GobConsole, cmd_varString));
+}
+
+GobConsole::~GobConsole() {
+}
+
+void GobConsole::preEnter() {
+}
+
+void GobConsole::postEnter() {
+}
+
+bool GobConsole::cmd_varSize(int argc, const char **argv) {
+ DebugPrintf("Size of the variable space: %d bytes\n", _vm->_inter->_variables->getSize());
+ return true;
+}
+
+bool GobConsole::cmd_var8(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var8 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if (varNum >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff8(varNum, varVal);
+ }
+
+ DebugPrintf("var8_%d = %d\n", varNum, _vm->_inter->_variables->readOff8(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_var16(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var16 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if ((varNum + 1) >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff16(varNum, varVal);
+ }
+
+ DebugPrintf("var16_%d = %d\n", varNum, _vm->_inter->_variables->readOff16(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_var32(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: var32 <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if ((varNum + 3) >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 varVal = atoi(argv[2]);
+ _vm->_inter->_variables->writeOff32(varNum, varVal);
+ }
+
+ DebugPrintf("var8_%d = %d\n", varNum, _vm->_inter->_variables->readOff32(varNum));
+
+ return true;
+}
+
+bool GobConsole::cmd_varString(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: varString <var offset> (<value>)\n");
+ return true;
+ }
+
+ uint32 varNum = atoi(argv[1]);
+
+ if (varNum >= _vm->_inter->_variables->getSize()) {
+ DebugPrintf("Variable offset out of range\n");
+ return true;
+ }
+
+ if (argc > 2) {
+ uint32 maxLength = _vm->_inter->_variables->getSize() - varNum;
+
+ Common::strlcpy(_vm->_inter->_variables->getAddressOffString(varNum), argv[2], maxLength);
+ }
+
+ DebugPrintf("varString_%d = \"%s\"\n", varNum, _vm->_inter->_variables->getAddressOffString(varNum));
+
+ return true;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/console.h b/engines/gob/console.h
new file mode 100644
index 0000000000..5dc6096062
--- /dev/null
+++ b/engines/gob/console.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$
+ *
+ */
+
+#ifndef GOB_CONSOLE_H
+#define GOB_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Gob {
+
+class GobEngine;
+
+class GobConsole : public GUI::Debugger {
+public:
+ GobConsole(GobEngine *vm);
+ virtual ~GobConsole(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ GobEngine *_vm;
+
+ bool cmd_varSize(int argc, const char **argv);
+ bool cmd_var8(int argc, const char **argv);
+ bool cmd_var16(int argc, const char **argv);
+ bool cmd_var32(int argc, const char **argv);
+ bool cmd_varString(int argc, const char **argv);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/gob/dataio.cpp b/engines/gob/dataio.cpp
index 694cec6a6f..55ca3350af 100644
--- a/engines/gob/dataio.cpp
+++ b/engines/gob/dataio.cpp
@@ -24,10 +24,10 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/dataio.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
@@ -393,7 +393,7 @@ int32 DataIO::getChunkSize(const char *chunkName, int32 &packSize) {
void DataIO::openDataFile(const char *src, bool itk) {
char path[128];
- strncpy0(path, src, 127);
+ Common::strlcpy(path, src, 128);
if (!strchr(path, '.')) {
path[123] = 0;
strcat(path, ".stk");
@@ -556,7 +556,7 @@ int32 DataIO::getDataSize(const char *name) {
int32 chunkSize;
int32 packSize = -1;
- strncpy0(buf, name, 127);
+ Common::strlcpy(buf, name, 128);
chunkSize = getChunkSize(buf, packSize);
if (chunkSize >= 0)
diff --git a/engines/gob/demos/demoplayer.cpp b/engines/gob/demos/demoplayer.cpp
index 25cd470f04..011c524798 100644
--- a/engines/gob/demos/demoplayer.cpp
+++ b/engines/gob/demos/demoplayer.cpp
@@ -28,7 +28,6 @@
#include "gob/gob.h"
#include "gob/demos/demoplayer.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/draw.h"
@@ -123,7 +122,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 +243,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 +297,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 907dc64295..43f15b8845 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",
@@ -2236,16 +2264,14 @@ 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!
- {
+ { // Supplied by scoriae
{
"fascination",
- "",
+ "VGA",
AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
- UNK_LANG,
+ EN_ANY,
kPlatformPC,
- ADGF_PIRATED,
+ ADGF_NO_FLAGS,
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeFascination,
@@ -2798,6 +2824,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",
@@ -3200,7 +3240,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3214,7 +3254,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3228,7 +3268,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3242,7 +3282,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3256,7 +3296,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3270,7 +3310,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3284,7 +3324,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3298,7 +3338,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3312,7 +3352,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3326,7 +3366,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3340,7 +3380,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3354,7 +3394,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3368,7 +3408,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3382,7 +3422,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3396,7 +3436,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3410,7 +3450,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3424,7 +3464,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3438,7 +3478,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3452,7 +3492,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3466,7 +3506,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3480,7 +3520,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3494,7 +3534,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3508,7 +3548,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3522,7 +3562,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by goodoldgeorg in bug report #2098838
@@ -3536,7 +3576,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3554,7 +3594,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 1
},
{
@@ -3568,7 +3608,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3582,7 +3622,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3596,7 +3636,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3610,7 +3650,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3624,7 +3664,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3638,7 +3678,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3652,7 +3692,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
"lda1.stk", 0, 0
},
{
@@ -3666,7 +3706,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
"lda1.stk", 0, 0
},
{
@@ -3680,7 +3720,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by gamin in the forums
@@ -3694,7 +3734,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3708,7 +3748,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by goodoldgeorg in bug report #2770340
@@ -3722,7 +3762,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{
@@ -3741,7 +3781,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeUrban,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesTrueColor | kFeaturesSCNDemo,
0, 0, 2
},
{
@@ -3759,7 +3799,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3777,7 +3817,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3795,7 +3835,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by scoriae in the forums
@@ -3813,7 +3853,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3836,7 +3876,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 3
},
{
@@ -3854,7 +3894,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 4
},
{
@@ -3876,7 +3916,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 5
},
{
@@ -3897,7 +3937,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 6
},
{
@@ -3915,7 +3955,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3933,7 +3973,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3951,7 +3991,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by scoriae in the forums
@@ -3969,7 +4009,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3987,7 +4027,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4005,7 +4045,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4023,7 +4063,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4041,7 +4081,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -4059,7 +4099,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4077,7 +4117,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ //Supplied by goodoldgeorg in bug report #2820006
@@ -4095,7 +4135,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4113,7 +4153,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4131,7 +4171,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeBambou,
- kFeatures640,
+ kFeatures640x480,
"intro.stk", "intro.tot", 0
},
{
@@ -4149,7 +4189,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4167,7 +4207,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4185,7 +4225,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4213,7 +4253,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.
@@ -4227,7 +4267,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4255,7 +4295,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4269,7 +4309,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4283,7 +4323,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4297,7 +4337,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4311,7 +4351,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4325,7 +4365,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4345,7 +4385,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi2,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 1
},
{
@@ -4359,7 +4399,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4373,7 +4413,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4387,7 +4427,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4401,7 +4441,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4415,7 +4455,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4429,7 +4469,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4485,7 +4525,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4513,7 +4553,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4527,7 +4567,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4825,7 +4865,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //9
@@ -4881,7 +4921,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeaturesCD,
+ kFeaturesCD | kFeaturesTrueColor,
0, 0, 0
},
{ //13
@@ -4895,7 +4935,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //14
@@ -4909,7 +4949,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //15
@@ -4923,7 +4963,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //16
@@ -4937,7 +4977,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //17
@@ -4951,7 +4991,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //18
@@ -4965,7 +5005,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //19
@@ -4979,7 +5019,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeBambou,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //20
@@ -5021,7 +5061,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", 0, 0
},
{ //23
@@ -5035,7 +5075,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
"adif41.stk", 0, 0
},
{ //24
@@ -5049,7 +5089,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 62b9a5e630..bb357168b7 100644
--- a/engines/gob/draw.cpp
+++ b/engines/gob/draw.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/draw.h"
@@ -263,10 +264,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 +301,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 +317,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) {
@@ -328,7 +329,7 @@ void Draw::adjustCoords(char adjust, int16 *coord1, int16 *coord2) {
if (coord2)
*coord2 *= 2;
if (coord1)
- *coord2 *= 2;
+ *coord1 *= 2;
break;
case 1:
@@ -381,14 +382,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++;
@@ -470,10 +471,8 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in
else
WRITE_VAR(24, (uint32) 0);
WRITE_VAR(25, (uint32) shortId);
- if (_hotspotText) {
- strncpy(_hotspotText, paramStr, 40);
- _hotspotText[39] = 0;
- }
+ if (_hotspotText)
+ Common::strlcpy(_hotspotText, paramStr, 40);
}
_vm->_inter->funcBlock(0);
_vm->_game->_script->pop();
@@ -548,14 +547,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);
}
@@ -602,7 +597,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;
@@ -626,16 +621,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 cbadde8e27..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,7 +187,7 @@ 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);
@@ -196,7 +196,7 @@ public:
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);
@@ -267,7 +267,7 @@ public:
virtual ~Draw_Fascination() {}
virtual void spriteOperation(int16 operation);
- void decompWin(int16 x, int16 y, SurfaceDescPtr destPtr);
+ void decompWin(int16 x, int16 y, SurfacePtr destPtr);
void drawWin(int16 fct);
void saveWin(int16 id);
void restoreWin(int16 id);
diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp
index dcb76d722c..86e5cb8314 100644
--- a/engines/gob/draw_fascin.cpp
+++ b/engines/gob/draw_fascin.cpp
@@ -40,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;
@@ -149,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);
@@ -177,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,
@@ -227,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,
@@ -238,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]);
}
}
@@ -255,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;
}
}
@@ -270,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);
}
@@ -308,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;
@@ -364,7 +345,7 @@ void Draw_Fascination::drawWin(int16 fct) {
int len;
Resource *resource;
int table[10];
- SurfaceDescPtr tempSrf;
+ SurfacePtr tempSrf;
if (_destSurface == kBackSurface) {
@@ -486,9 +467,9 @@ void Draw_Fascination::drawWin(int16 fct) {
}
if ((_sourceSurface == kBackSurface) && (fct == 0)) {
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
_spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
- _spriteTop + _spriteBottom - 1, _destSpriteX, _destSpriteY, _transparency);
+ _spriteTop + _spriteBottom - 1, _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
if (!found)
return;
@@ -498,18 +479,18 @@ void Draw_Fascination::drawWin(int16 fct) {
for (int i = 9; i >= j; i--) {
if (table[i])
- _vm->_video->drawSprite(*_fascinWin[table[i]].savedSurface, *_spritesArray[_destSurface],
+ _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, 0);
+ _fascinWin[table[i]].top - _spriteTop + _destSpriteY);
}
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);
+ tempSrf->blit(*_backSurface, left, top, width, height, 0, 0);
int max = 0;
if (_vm->_global->_curWinId != 0)
@@ -517,37 +498,37 @@ void Draw_Fascination::drawWin(int16 fct) {
for (int i = 9; i >= max; i--) {
if (table[i])
- _vm->_video->drawSprite(*_fascinWin[table[i]].savedSurface, *tempSrf,
+ 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 , 0);
+ _fascinWin[table[i]].top - top);
}
invalidateRect(left, top, width, height);
switch (fct) {
case DRAW_BLITSURF: // 0 - move
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *tempSrf,
+ tempSrf->blit(*_spritesArray[_sourceSurface],
_spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
- _spriteTop + _spriteBottom - 1, 0, 0, _transparency);
+ _spriteTop + _spriteBottom - 1, 0, 0, (_transparency == 0) ? -1 : 0);
break;
case DRAW_PUTPIXEL: // 1 - put a pixel
- _vm->_video->putPixel(0, 0, _frontColor, *tempSrf);
+ tempSrf->putPixel(0, 0, _frontColor);
break;
case DRAW_FILLRECT: // 2 - fill rectangle
- _vm->_video->fillRect(*tempSrf, 0, 0, _spriteRight - 1, _spriteBottom - 1, _backColor);
+ tempSrf->fillRect(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);
+ tempSrf->drawLine(0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
break;
case DRAW_INVALIDATE: // 4 - Draw a circle
- _vm->_video->drawCircle(*tempSrf, _spriteRight, _spriteRight, _spriteRight, _frontColor);
+ tempSrf->drawCircle(_spriteRight, _spriteRight, _spriteRight, _frontColor);
break;
case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
@@ -557,40 +538,40 @@ void Draw_Fascination::drawWin(int16 fct) {
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);
+ _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
- _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);
+ 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)
- _vm->_video->fillRect(*tempSrf, 0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _backColor);
+ tempSrf->fillRect(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);
+ tempSrf->fillRect(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);
+ _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;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite], *tempSrf,
+ tempSrf->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite],
xx, yy, xx + _fontToSprite[_fontIndex].width - 1,
- yy + _fontToSprite[_fontIndex].height - 1, 0, 0, _transparency);
+ yy + _fontToSprite[_fontIndex].height - 1, 0, 0, (_transparency == 0) ? -1 : 0);
}
break;
@@ -606,26 +587,26 @@ void Draw_Fascination::drawWin(int16 fct) {
for (; i < 10; i++) {
if (table[i]) {
int k = table[i];
- _vm->_video->drawSprite(*tempSrf, *_fascinWin[k].savedSurface,
+ _fascinWin[k].savedSurface->blit(*tempSrf,
0, 0, width - left, height - top,
left - _fascinWin[k].left + (_fascinWin[k].left & 7),
- top - _fascinWin[k].top, 0);
+ top - _fascinWin[k].top);
// Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *tempSrf,
+ 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, 0);
+ MAX(top , _fascinWin[k].top) - top);
if (_cursorIndex != -1)
- _vm->_video->drawSprite(*_cursorSpritesBack, *tempSrf,
+ tempSrf->blit(*_cursorSpritesBack,
0, 0, _cursorWidth - 1, _cursorHeight - 1,
- _cursorX - left, _cursorY - top, 0);
+ _cursorX - left, _cursorY - top);
for (int j = 9; j > i; j--) {
if (table[j] && overlapWin(k, table[j])) {
int l = table[j];
- _vm->_video->drawSprite(*_fascinWin[l].savedSurface, *tempSrf,
+ 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,
@@ -634,37 +615,37 @@ void Draw_Fascination::drawWin(int16 fct) {
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);
+ MAX(_fascinWin[l].top , _fascinWin[k].top ) - top);
}
}
}
}
- _vm->_video->drawSprite(*tempSrf, *_backSurface, 0, 0, width - left, height - top, left, top, 0);
+ _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
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *_backSurface,
+ _backSurface->blit(*_spritesArray[_sourceSurface],
_spriteLeft, _spriteTop,
_spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
break;
case DRAW_PUTPIXEL: // 1 - put a pixel
- _vm->_video->putPixel(_destSpriteX, _destSpriteY, _frontColor, *_backSurface);
+ _backSurface->putPixel(_destSpriteX, _destSpriteY, _frontColor);
break;
case DRAW_FILLRECT: // 2 - fill rectangle
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1, _backColor);
+ _backSurface->fillRect(_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);
+ _backSurface->drawLine(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _frontColor);
break;
case DRAW_INVALIDATE: // 4 - Draw a circle
- _vm->_video->drawCircle(*_backSurface, _spriteRight, _spriteRight, _spriteRight, _frontColor);
+ _backSurface->drawCircle(_spriteRight, _spriteRight, _spriteRight, _frontColor);
break;
case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
@@ -674,42 +655,43 @@ void Draw_Fascination::drawWin(int16 fct) {
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);
+ _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
- _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);
+ _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)
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
+ _backSurface->fillRect(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
break;
case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
+ _backSurface->fillRect(_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]);
+ _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;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite], *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite],
xx, yy,
xx + _fontToSprite[_fontIndex].width - 1,
yy + _fontToSprite[_fontIndex].height - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
}
break;
@@ -736,7 +718,7 @@ void Draw_Fascination::drawWin(int16 fct) {
}
}
-void Draw_Fascination::decompWin(int16 x, int16 y, SurfaceDescPtr destPtr) {
+void Draw_Fascination::decompWin(int16 x, int16 y, SurfacePtr destPtr) {
Resource *resource;
resource = _vm->_game->_resources->getResource((uint16) _spriteLeft,
&_spriteRight, &_spriteBottom);
@@ -884,11 +866,11 @@ void Draw_Fascination::moveWin(int16 id) {
saveWin(id);
// Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *_backSurface,
+ _backSurface->blit(*_frontSurface,
oldLeft, oldTop,
oldLeft + _fascinWin[id].width - 1,
oldTop + _fascinWin[id].height - 1,
- _fascinWin[id].left, _fascinWin[id].top, 0);
+ _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);
@@ -909,8 +891,8 @@ void Draw_Fascination::activeWin(int16 id) {
int16 t[10], t2[10];
int nextId = -1;
int oldId = -1;
- SurfaceDescPtr tempSrf;
- SurfaceDescPtr oldSrf[10];
+ SurfacePtr tempSrf;
+ SurfacePtr oldSrf[10];
if (_fascinWin[id].id == -1)
return;
@@ -934,11 +916,11 @@ void Draw_Fascination::activeWin(int16 id) {
for (int i = 9; i >= 0; i--) {
if (t[i] != -1) {
if (nextId != -1)
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
+ _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, 0);
+ _fascinWin[t[i]].left & 7, 0);
t2[i] = nextId;
restoreWin(t[i]);
nextId = t[i];
@@ -946,35 +928,35 @@ void Draw_Fascination::activeWin(int16 id) {
}
oldId = nextId;
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
+ _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, 0);
+ _fascinWin[id].left & 7, 0);
restoreWin(id);
nextId = id;
for (int i = 0; i < 10; i++) {
if (t[i] != -1) {
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
+ _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, 0);
+ _fascinWin[t[i]].left & 7, 0);
oldSrf[t[i]] = _fascinWin[nextId].savedSurface;
if (t2[i] != -1)
- _vm->_video->drawSprite(*_fascinWin[t2[i]].savedSurface, *_backSurface,
+ _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, 0);
+ _fascinWin[t[i]].top);
else {
// Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *_backSurface,
+ _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, 0);
+ _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,
@@ -984,16 +966,16 @@ void Draw_Fascination::activeWin(int16 id) {
}
tempSrf = _vm->_video->initSurfDesc(_vm->_global->_videoMode, _winMaxWidth + 7, _winMaxHeight, 0);
- _vm->_video->drawSprite(*_backSurface, *tempSrf,
+ 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, 0);
- _vm->_video->drawSprite(*_fascinWin[oldId].savedSurface, *_backSurface,
+ _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, 0);
+ _fascinWin[id].left, _fascinWin[id].top);
_fascinWin[oldId].savedSurface.reset();
_fascinWin[oldId].savedSurface = tempSrf;
@@ -1026,18 +1008,18 @@ void Draw_Fascination::closeAllWin() {
}
void Draw_Fascination::saveWin(int16 id) {
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[id].savedSurface,
+ _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, 0);
+ _fascinWin[id].left & 7, 0);
}
void Draw_Fascination::restoreWin(int16 id) {
- _vm->_video->drawSprite(*_fascinWin[id].savedSurface, *_backSurface,
+ _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, 0);
+ _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);
@@ -1049,14 +1031,19 @@ void Draw_Fascination::drawWinTrace(int16 left, int16 top, int16 width, int16 he
right = left + width - 1;
bottom = top + height - 1;
- for (int32 x = left; x < right; x++) {
- _frontSurface->getVidMem()[_frontSurface->getWidth() * top + x] = (128 + _frontSurface->getVidMem()[_frontSurface->getWidth() * top + x]) & 0xff;
- _frontSurface->getVidMem()[_frontSurface->getWidth() * bottom + x] = (128 + _frontSurface->getVidMem()[_frontSurface->getWidth() * bottom + x]) & 0xff;
+ 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);
}
- for (int32 y = top; y < bottom; y++) {
- _frontSurface->getVidMem()[_frontSurface->getWidth() * y + left] = (128 + _frontSurface->getVidMem()[_frontSurface->getWidth() * y + left]) & 0xff;
- _frontSurface->getVidMem()[_frontSurface->getWidth() * y + right] = (128 + _frontSurface->getVidMem()[_frontSurface->getWidth() * y + right]) & 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();
diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 583d13986e..fc5521a959 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);
@@ -152,26 +151,26 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
case DRAW_PUTPIXEL:
switch (_pattern & 0xFF) {
- case -1:
+ case 0xFF:
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..d130f424c2 100644
--- a/engines/gob/draw_v1.cpp
+++ b/engines/gob/draw_v1.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "graphics/cursorman.h"
#include "gob/gob.h"
#include "gob/draw.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/game.h"
@@ -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;
@@ -258,7 +258,7 @@ void Draw_v1::printTotText(int16 id) {
} else if (cmd == 1) {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
- strncpy0(buf, GET_VARO_STR(val), 19);
+ Common::strlcpy(buf, GET_VARO_STR(val), 20);
} else {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
@@ -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 4ecb759e9d..ec678644d5 100644
--- a/engines/gob/draw_v2.cpp
+++ b/engines/gob/draw_v2.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "graphics/cursorman.h"
#include "gob/gob.h"
#include "gob/draw.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/game.h"
@@ -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) {
@@ -550,7 +550,7 @@ void Draw_v2::printTotText(int16 id) {
sprintf(buf, "%d", VAR_OFFSET(val));
} else if (cmd == 1) {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
- strncpy0(buf, GET_VARO_STR(val), 19);
+ Common::strlcpy(buf, GET_VARO_STR(val), 20);
} else {
val = READ_LE_UINT16(ptrEnd + 18) * 4;
sprintf(buf, "%d", VAR_OFFSET(val));
@@ -617,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;
@@ -718,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);
@@ -746,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,
@@ -796,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,
@@ -807,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]);
}
}
@@ -824,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;
}
}
@@ -839,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);
}
@@ -877,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);
}
@@ -887,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/game.cpp b/engines/gob/game.cpp
index b5025ba76f..98c1066cb0 100644
--- a/engines/gob/game.cpp
+++ b/engines/gob/game.cpp
@@ -24,10 +24,10 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/game.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/variables.h"
@@ -214,7 +214,7 @@ void Game::prepareStart() {
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
_vm->_draw->initScreen();
- _vm->_video->fillRect(*_vm->_draw->_frontSurface, 0, 0,
+ _vm->_draw->_frontSurface->fillRect(0, 0,
_vm->_video->_surfWidth - 1, _vm->_video->_surfHeight - 1, 1);
_vm->_util->setMousePos(152, 92);
@@ -247,7 +247,7 @@ void Game::playTot(int16 skipPlay) {
int16 *oldCaptureCounter;
int16 *oldBreakFrom;
int16 *oldNestLevel;
- int16 _captureCounter;
+ int16 captureCounter = 0;
int16 breakFrom;
int16 nestLevel;
@@ -259,7 +259,7 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->_nestLevel = &nestLevel;
_vm->_inter->_breakFromLevel = &breakFrom;
- _vm->_scenery->_pCaptureCounter = &_captureCounter;
+ _vm->_scenery->_pCaptureCounter = &captureCounter;
strcpy(savedTotName, _curTotFile);
if (skipPlay <= 0) {
@@ -319,10 +319,12 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->renewTimeInVars();
- WRITE_VAR(13, _vm->_global->_useMouse);
- WRITE_VAR(14, _vm->_global->_soundFlags);
- WRITE_VAR(15, _vm->_global->_fakeVideoMode);
- WRITE_VAR(16, _vm->_global->_language);
+ if (_vm->_inter->_variables) {
+ WRITE_VAR(13, _vm->_global->_useMouse);
+ WRITE_VAR(14, _vm->_global->_soundFlags);
+ WRITE_VAR(15, _vm->_global->_fakeVideoMode);
+ WRITE_VAR(16, _vm->_global->_language);
+ }
_vm->_inter->callSub(2);
@@ -357,7 +359,8 @@ void Game::playTot(int16 skipPlay) {
if (_totToLoad[0] == 0)
break;
- strcpy(_curTotFile, _totToLoad);
+ Common::strlcpy(_curTotFile, _totToLoad, 14);
+
}
} else {
_vm->_inter->initControlVars(0);
@@ -370,7 +373,7 @@ void Game::playTot(int16 skipPlay) {
_vm->_inter->_terminate = 2;
}
- strcpy(_curTotFile, savedTotName);
+ Common::strlcpy(_curTotFile, savedTotName, 14);
_vm->_inter->_nestLevel = oldNestLevel;
_vm->_inter->_breakFromLevel = oldBreakFrom;
@@ -575,7 +578,7 @@ void Game::totSub(int8 flags, const char *newTotFile) {
if (flags & 1)
_vm->_inter->_variables = 0;
- strncpy0(_curTotFile, newTotFile, 9);
+ Common::strlcpy(_curTotFile, newTotFile, 10);
strcat(_curTotFile, ".TOT");
if (_vm->_inter->_terminate != 0) {
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 02a8af92bc..a1d3b1a61d 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -136,6 +136,8 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) {
_copyProtection = ConfMan.getBool("copy_protection");
+ _console = new GobConsole(this);
+
DebugMan.addDebugChannel(kDebugFuncOp, "FuncOpcodes", "Script FuncOpcodes debug level");
DebugMan.addDebugChannel(kDebugDrawOp, "DrawOpcodes", "Script DrawOpcodes debug level");
DebugMan.addDebugChannel(kDebugGobOp, "GoblinOpcodes", "Script GoblinOpcodes debug level");
@@ -153,6 +155,8 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) {
}
GobEngine::~GobEngine() {
+ delete _console;
+
deinitGameParts();
}
@@ -209,10 +213,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 +225,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())
@@ -518,22 +532,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 +554,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..fe69e27c01 100644
--- a/engines/gob/gob.h
+++ b/engines/gob/gob.h
@@ -32,6 +32,8 @@
#include "engines/engine.h"
+#include "gob/console.h"
+
namespace GUI {
class StaticTextWidget;
}
@@ -41,8 +43,14 @@ namespace GUI {
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Gobliiins
+ * - Gobliins 2
+ * - Goblins 3
+ * - Ween: The Prophecy
+ * - Bargon Attack
+ * - Lost in Time
+ * - The Bizarre Adventures of Woodruff and the Schnibble
*/
namespace Gob {
@@ -62,6 +70,7 @@ class PalAnim;
class Scenery;
class Util;
class SaveLoad;
+class GobConsole;
#define WRITE_VAR_UINT32(var, val) _vm->_inter->_variables->writeVar32(var, val)
#define WRITE_VAR_UINT16(var, val) _vm->_inter->_variables->writeVar16(var, val)
@@ -119,14 +128,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 {
@@ -151,6 +161,7 @@ private:
GameType _gameType;
int32 _features;
Common::Platform _platform;
+ GobConsole *_console;
uint32 _pauseStart;
@@ -163,6 +174,8 @@ private:
bool initGameParts();
void deinitGameParts();
+ bool initGraphics();
+
public:
static const Common::Language _gobToScummVMLang[];
@@ -173,6 +186,8 @@ public:
uint16 _height;
uint8 _mode;
+ Graphics::PixelFormat _pixelFormat;
+
Common::String _startStk;
Common::String _startTot;
uint32 _demoIndex;
@@ -208,13 +223,18 @@ 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;
+ GUI::Debugger *getDebugger() { return _console; }
+
+ const Graphics::PixelFormat &getPixelFormat() const;
+
GobEngine(OSystem *syst);
virtual ~GobEngine();
diff --git a/engines/gob/goblin.cpp b/engines/gob/goblin.cpp
index 11043df782..88827e7fcf 100644
--- a/engines/gob/goblin.cpp
+++ b/engines/gob/goblin.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "common/str.h"
+
#include "gob/gob.h"
#include "gob/goblin.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/draw.h"
@@ -233,9 +234,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);
@@ -668,11 +669,11 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
resDelta = i;
}
- for (i = 1; ((i + _pressedMapX) < _vm->_map->_mapWidth) &&
+ for (i = 1; ((i + _pressedMapX) < _vm->_map->getMapWidth()) &&
(_vm->_map->getPass(_pressedMapX + i, _pressedMapY) == 0); i++)
;
- if ((_pressedMapX + i) < _vm->_map->_mapWidth) {
+ if ((_pressedMapX + i) < _vm->_map->getMapWidth()) {
deltaPix = (i * 12) - (posX % 12);
if ((resDelta == -1) || (deltaPix < resDeltaPix)) {
resDeltaPix = deltaPix;
@@ -681,11 +682,11 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
}
}
- for (i = 1; ((i + _pressedMapY) < _vm->_map->_mapHeight) &&
+ for (i = 1; ((i + _pressedMapY) < _vm->_map->getMapHeight()) &&
(_vm->_map->getPass(_pressedMapX, _pressedMapY + i) == 0); i++)
;
- if ((_pressedMapY + i) < _vm->_map->_mapHeight) {
+ if ((_pressedMapY + i) < _vm->_map->getMapHeight()) {
deltaPix = (i * 6) - (posY % 6);
if ((resDelta == -1) || (deltaPix < resDeltaPix)) {
resDeltaPix = deltaPix;
@@ -726,8 +727,8 @@ void Goblin::adjustDest(int16 posX, int16 posY) {
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::adjustTarget() {
@@ -737,18 +738,18 @@ void Goblin::adjustTarget() {
if ((_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX, _pressedMapY - 1) != 0)) {
_pressedMapY--;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY) != 0)) {
_pressedMapX++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY - 1) != 0)) {
_pressedMapY--;
_pressedMapX++;
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::targetDummyItem(Gob_Object *gobDesc) {
@@ -847,7 +848,7 @@ void Goblin::targetItem() {
}
}
- if (_pressedMapY < (_vm->_map->_mapHeight-1)) {
+ if (_pressedMapY < (_vm->_map->getMapHeight()-1)) {
if ((_vm->_map->getItem(_pressedMapX, _pressedMapY + 1)) ==
(_vm->_map->getItem(_pressedMapX, _pressedMapY))) {
_pressedMapY++;
@@ -898,8 +899,8 @@ void Goblin::targetItem() {
}
}
}
- _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP((int) _pressedMapX, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP((int) _pressedMapY, 0, _vm->_map->getMapHeight() - 1);
}
void Goblin::moveFindItem(int16 posX, int16 posY) {
@@ -933,23 +934,23 @@ void Goblin::moveFindItem(int16 posX, int16 posY) {
break;
}
- _pressedMapX = CLIP(posX / 12, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP(posY / 6, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP(posX / 12, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP(posY / 6, 0, _vm->_map->getMapHeight() - 1);
if ((_vm->_map->getItem(_pressedMapX, _pressedMapY) == 0) && (i < 20)) {
- if ((_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ if ((_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX, _pressedMapY + 1) != 0)) {
_pressedMapY++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
- (_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
+ (_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY + 1) != 0)) {
_pressedMapX++;
_pressedMapY++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY) != 0)) {
_pressedMapX++;
- } else if ((_pressedMapX < (_vm->_map->_mapWidth - 1)) &&
+ } else if ((_pressedMapX < (_vm->_map->getMapWidth() - 1)) &&
(_pressedMapY > 0) &&
(_vm->_map->getItem(_pressedMapX + 1, _pressedMapY - 1) != 0)) {
_pressedMapX++;
@@ -965,15 +966,15 @@ void Goblin::moveFindItem(int16 posX, int16 posY) {
(_vm->_map->getItem(_pressedMapX - 1, _pressedMapY) != 0)) {
_pressedMapX--;
} else if ((_pressedMapX > 0) &&
- (_pressedMapY < (_vm->_map->_mapHeight - 1)) &&
+ (_pressedMapY < (_vm->_map->getMapHeight() - 1)) &&
(_vm->_map->getItem(_pressedMapX - 1, _pressedMapY + 1) != 0)) {
_pressedMapX--;
_pressedMapY++;
}
}
} else {
- _pressedMapX = CLIP(posX / 12, 0, _vm->_map->_mapWidth - 1);
- _pressedMapY = CLIP(posY / 6, 0, _vm->_map->_mapHeight - 1);
+ _pressedMapX = CLIP(posX / 12, 0, _vm->_map->getMapWidth() - 1);
+ _pressedMapY = CLIP(posY / 6, 0, _vm->_map->getMapHeight() - 1);
}
}
@@ -1186,7 +1187,7 @@ void Goblin::loadObjects(const char *source) {
freeObjects();
initList();
- strncpy0(_vm->_map->_sourceFile, source, 14);
+ Common::strlcpy(_vm->_map->_sourceFile, source, 15);
_vm->_map->_sourceFile[strlen(_vm->_map->_sourceFile) - 4] = 0;
_vm->_map->loadMapObjects(source);
@@ -1386,8 +1387,8 @@ void Goblin::pickItem(int16 indexToPocket, int16 idToPocket) {
_itemIndInPocket = indexToPocket;
_itemIdInPocket = idToPocket;
- for (int y = 0; y < _vm->_map->_mapHeight; y++) {
- for (int x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (int y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (int x = 0; x < _vm->_map->getMapWidth(); x++) {
if (_itemByteFlag == 1) {
if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == idToPocket)
_vm->_map->setItem(x, y, _vm->_map->getItem(x, y) & 0xFF);
@@ -1450,7 +1451,7 @@ void Goblin::placeItem(int16 indexInPocket, int16 idInPocket) {
_vm->_map->placeItem(xPos, yPos - 1, idInPocket);
if (lookDir == 4) {
- if (xPos < _vm->_map->_mapWidth - 1) {
+ if (xPos < _vm->_map->getMapWidth() - 1) {
_vm->_map->placeItem(xPos + 1, yPos, idInPocket);
if (yPos > 0)
@@ -1497,16 +1498,16 @@ void Goblin::swapItems(int16 indexToPick, int16 idToPick) {
_itemIdInPocket = idToPick;
if (_itemByteFlag == 0) {
- for (y = 0; y < _vm->_map->_mapHeight; y++) {
- for (x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (x = 0; x < _vm->_map->getMapWidth(); x++) {
if ((_vm->_map->getItem(x, y) & 0xFF) == idToPick)
_vm->_map->setItem(x, y, (_vm->_map->getItem(x, y) & 0xFF00) + idToPlace);
}
}
} else {
- for (y = 0; y < _vm->_map->_mapHeight; y++) {
- for (x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (x = 0; x < _vm->_map->getMapWidth(); x++) {
if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == idToPick)
_vm->_map->setItem(x, y, (_vm->_map->getItem(x, y) & 0xFF) + (idToPlace << 8));
}
@@ -1704,15 +1705,15 @@ void Goblin::setState(int16 index, int16 state) {
animData->newCycle = _vm->_scenery->getAnimLayer(animation, layer)->framesCount;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 1);
- if (_vm->_map->_bigTiles) {
- *obj->pPosY = ((obj->goblinY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles()) {
+ *obj->pPosY = ((obj->goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
(obj->goblinY + 1) / 2;
} else {
- *obj->pPosY = (obj->goblinY + 1) * _vm->_map->_tilesHeight -
+ *obj->pPosY = (obj->goblinY + 1) * _vm->_map->getTilesHeight() -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
}
- *obj->pPosX = obj->goblinX * _vm->_map->_tilesWidth;
+ *obj->pPosX = obj->goblinX * _vm->_map->getTilesWidth();
}
void Goblin::animate(Mult::Mult_Object *obj) {
@@ -1780,40 +1781,69 @@ void Goblin::animate(Mult::Mult_Object *obj) {
}
void Goblin::move(int16 destX, int16 destY, int16 objIndex) {
- Mult::Mult_Object *obj;
- Mult::Mult_AnimData *animData;
- int16 mouseX;
- int16 mouseY;
- int16 gobDestX;
- int16 gobDestY;
- obj = &_vm->_mult->_objects[objIndex];
- animData = obj->pAnimData;
+ Mult::Mult_Object *obj = &_vm->_mult->_objects[objIndex];
+ Mult::Mult_AnimData *animData = obj->pAnimData;
- obj->gobDestX = destX;
- obj->gobDestY = destY;
+ obj->gobDestX = destX;
+ obj->gobDestY = destY;
animData->destX = destX;
animData->destY = destY;
if (animData->isBusy != 0) {
if ((destX == -1) && (destY == -1)) {
- mouseX = _vm->_global->_inter_mouseX;
- mouseY = _vm->_global->_inter_mouseY;
- if (_vm->_map->_bigTiles)
- mouseY += ((_vm->_global->_inter_mouseY / _vm->_map->_tilesHeight) + 1) / 2;
+ int16 mouseX = _vm->_global->_inter_mouseX;
+ int16 mouseY = _vm->_global->_inter_mouseY;
+
+ if (_vm->_map->hasBigTiles())
+ mouseY += ((_vm->_global->_inter_mouseY / _vm->_map->getTilesHeight()) + 1) / 2;
- gobDestX = mouseX / _vm->_map->_tilesWidth;
- gobDestY = mouseY / _vm->_map->_tilesHeight;
+ int16 gobDestX = mouseX / _vm->_map->getTilesWidth();
+ int16 gobDestY = mouseY / _vm->_map->getTilesHeight();
if (_vm->_map->getPass(gobDestX, gobDestY) == 0)
_vm->_map->findNearestWalkable(gobDestX, gobDestY, mouseX, mouseY);
- animData->destX = obj->gobDestX =
- (gobDestX == -1) ? obj->goblinX : gobDestX;
- animData->destY = obj->gobDestY =
- (gobDestY == -1) ? obj->goblinY : gobDestY;
+ obj->gobDestX = (gobDestX == -1) ? obj->goblinX : gobDestX;
+ obj->gobDestY = (gobDestY == -1) ? obj->goblinY : gobDestY;
+
+ animData->destX = obj->gobDestX;
+ animData->destY = obj->gobDestY;
+ }
+ }
+
+ WRITE_VAR(56, 0);
+
+ byte passType = _vm->_map->getPass(obj->gobDestX, obj->gobDestY);
+
+ // Prevent continous walking on wide stairs
+ if (passType == 11) {
+ if (_vm->_map->getScreenWidth() == 640) {
+ obj->gobDestY++;
+ animData->destY++;
}
}
+
+ // Prevent stopping in the middle of big ladders
+ if ((passType == 19) || (passType == 20)) {
+ int ladderTop = 0;
+ while (_vm->_map->getPass(obj->gobDestX, obj->gobDestY + ladderTop) == passType)
+ ladderTop++;
+
+ int ladderBottom = 0;
+ while (_vm->_map->getPass(obj->gobDestX, obj->gobDestY + ladderBottom) == passType)
+ ladderBottom--;
+
+ int ladderDest;
+ if (ABS(ladderBottom) <= ladderTop)
+ ladderDest = obj->gobDestY + ladderBottom;
+ else
+ ladderDest = obj->gobDestY + ladderTop;
+
+ obj->gobDestY = ladderDest;
+ animData->destY = ladderDest;
+ }
+
initiateMove(obj);
}
diff --git a/engines/gob/goblin_v1.cpp b/engines/gob/goblin_v1.cpp
index f55fec433c..3dc4c6611d 100644
--- a/engines/gob/goblin_v1.cpp
+++ b/engines/gob/goblin_v1.cpp
@@ -154,8 +154,10 @@ void Goblin_v1::initiateMove(Mult::Mult_Object *obj) {
_vm->_map->_nearestWayPoint, _vm->_map->_nearestDest) == 0) {
_pathExistence = 0;
} else {
- _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
}
}
}
@@ -173,10 +175,10 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
_pathExistence = 0;
}
- nextAct = _vm->_map->getDirection(_vm->_map->_curGoblinX,
+ nextAct = (int16) _vm->_map->getDirection(_vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _vm->_map->_destX, _vm->_map->_destY);
- if (nextAct == 0)
+ if (nextAct == kDirNone)
_pathExistence = 0;
} else if (_pathExistence == 3) {
_vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
@@ -199,20 +201,20 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest) {
_vm->_map->optimizePoints(0, 0, 0);
- _vm->_map->_destX =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest)
_vm->_map->_nearestWayPoint--;
} else if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest) {
_vm->_map->optimizePoints(0, 0, 0);
- _vm->_map->_destX =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY =
- _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest)
_vm->_map->_nearestWayPoint++;
@@ -220,8 +222,12 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
if ((_vm->_map->checkDirectPath(0, _vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _gobDestX, _gobDestY) == 3) &&
(_vm->_map->getPass(_pressedMapX, _pressedMapY) != 0)) {
- _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
- _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(_vm->_map->_nearestWayPoint);
+
+ _vm->_map->_destX = wayPoint.x;
+ _vm->_map->_destY = wayPoint.y;
+
} else {
_pathExistence = 1;
_vm->_map->_destX = _pressedMapX;
@@ -229,7 +235,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
}
}
}
- nextAct = _vm->_map->getDirection(_vm->_map->_curGoblinX,
+ nextAct = (int16) _vm->_map->getDirection(_vm->_map->_curGoblinX,
_vm->_map->_curGoblinY, _vm->_map->_destX, _vm->_map->_destY);
}
}
@@ -238,11 +244,11 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
nextAct = 0x4DC8;
switch (nextAct) {
- case Map::kDirW:
+ case kDirW:
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
break;
@@ -254,7 +260,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = 23;
break;
- case Map::kDirN:
+ case kDirN:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -275,7 +281,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 2);
break;
- case Map::kDirS:
+ case kDirS:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -296,7 +302,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 6);
break;
- case Map::kDirSE:
+ case kDirSE:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX + 1, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -310,7 +316,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX - 1, _vm->_map->_curGoblinY + 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -324,7 +330,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirNW:
+ case kDirNW:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX - 1, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
@@ -338,7 +344,7 @@ void Goblin_v1::movePathFind(Mult::Mult_Object *obj,
gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
break;
- case Map::kDirNE:
+ case kDirNE:
if ((_vm->_map->getPass(_vm->_map->_curGoblinX + 1, _vm->_map->_curGoblinY - 1) == 6) &&
(_currentGoblin != 1)) {
_pathExistence = 0;
diff --git a/engines/gob/goblin_v2.cpp b/engines/gob/goblin_v2.cpp
index 2747750abb..503377c19b 100644
--- a/engines/gob/goblin_v2.cpp
+++ b/engines/gob/goblin_v2.cpp
@@ -39,10 +39,10 @@ namespace Gob {
Goblin_v2::Goblin_v2(GobEngine *vm) : Goblin_v1(vm) {
_gobsCount = -1;
- _rotStates[0][0] = 0; _rotStates[0][1] = 18; _rotStates[0][2] = 19; _rotStates[0][3] = 20;
- _rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
- _rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
- _rotStates[3][0] = 23; _rotStates[3][1] = 21; _rotStates[3][2] = 22; _rotStates[3][3] = 6;
+ _rotStates[0][0] = 0; _rotStates[0][1] = 18; _rotStates[0][2] = 19; _rotStates[0][3] = 20;
+ _rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
+ _rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
+ _rotStates[3][0] = 23; _rotStates[3][1] = 21; _rotStates[3][2] = 22; _rotStates[3][3] = 6;
}
void Goblin_v2::freeObjects() {
@@ -80,13 +80,13 @@ void Goblin_v2::placeObject(Gob_Object *objDesc, char animated,
objAnim->newCycle = 0;
_vm->_scenery->updateAnim(objAnim->layer, 0, objAnim->animation, 0,
*obj->pPosX, *obj->pPosY, 0);
- if (!_vm->_map->_bigTiles)
- *obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *obj->pPosY = (y + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *obj->pPosY = ((y + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((y + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (y + 1) / 2;
- *obj->pPosX = x * _vm->_map->_tilesWidth;
+ *obj->pPosX = x * _vm->_map->getTilesWidth();
} else {
if ((obj->goblinStates != 0) && (obj->goblinStates[state] != 0)) {
layer = obj->goblinStates[state][0].layer;
@@ -99,13 +99,13 @@ void Goblin_v2::placeObject(Gob_Object *objDesc, char animated,
objAnim->isStatic = 0;
objAnim->newCycle = _vm->_scenery->getAnimLayer(animation, layer)->framesCount;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (!_vm->_map->_bigTiles)
- *obj->pPosY = (y + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *obj->pPosY = (y + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *obj->pPosY = ((y + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((y + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (y + 1) / 2;
- *obj->pPosX = x * _vm->_map->_tilesWidth;
+ *obj->pPosX = x * _vm->_map->getTilesWidth();
initiateMove(obj);
} else
initiateMove(obj);
@@ -121,54 +121,55 @@ void Goblin_v2::initiateMove(Mult::Mult_Object *obj) {
obj->pAnimData->pathExistence = _vm->_map->checkDirectPath(obj,
obj->goblinX, obj->goblinY, obj->gobDestX, obj->gobDestY);
if (obj->pAnimData->pathExistence == 3) {
- obj->destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- obj->destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ obj->destX = wayPoint.x;
+ obj->destY = wayPoint.y;
}
}
void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16 nextAct) {
- Mult::Mult_AnimData *animData;
- int16 framesCount;
- int16 gobX;
- int16 gobY;
- int16 gobDestX;
- int16 gobDestY;
- int16 destX;
- int16 destY;
- int16 dir;
-
- dir = 0;
- animData = obj->pAnimData;
- framesCount = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
- animData->newCycle = framesCount;
- gobX = obj->goblinX;
- gobY = obj->goblinY;
- animData->order = gobY;
- gobDestX = obj->gobDestX;
- gobDestY = obj->gobDestY;
+ Mult::Mult_AnimData *animData = obj->pAnimData;
+
+ animData->newCycle = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
+
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY;
+ int16 destX = obj->destX;
+ int16 destY = obj->destY;
+ int16 gobDestX = obj->gobDestX;
+ int16 gobDestY = obj->gobDestY;
+
animData->destX = gobDestX;
animData->destY = gobDestY;
- destX = obj->destX;
- destY = obj->destY;
+ animData->order = gobY;
+
+ Direction dir = kDirNone;
if (animData->pathExistence == 1) {
+
dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
- if (dir == 0)
+ if (dir == kDirNone)
animData->pathExistence = 0;
- if ((gobX == destX) && (gobY == destY))
+ if ((gobX == gobDestX) && (gobY == gobDestY))
animData->pathExistence = 4;
+
} else if (animData->pathExistence == 3) {
- if ((gobX == gobDestX) && (gobY == gobDestY)) {
- animData->pathExistence = 4;
- destX = gobDestX;
- destY = gobDestY;
- } else {
+
+ if ((gobX != gobDestX) || (gobY != gobDestY)) {
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) != 1) {
+
if ((gobX == destX) && (gobY == destY)) {
+
if (obj->nearestWayPoint > obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -177,8 +178,12 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->nearestWayPoint--;
} else if (obj->nearestWayPoint < obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -188,8 +193,12 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
} else {
if ((_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) == 3) &&
(_vm->_map->getPass(gobDestX, gobDestY) != 0)) {
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
WRITE_VAR(56, 1);
} else {
animData->pathExistence = 1;
@@ -197,26 +206,35 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
destY = gobDestY;
}
}
+
}
+
} else {
destX = gobDestX;
destY = gobDestY;
}
+
dir = _vm->_map->getDirection(gobX, gobY, destX, destY);
+
+ } else {
+ animData->pathExistence = 4;
+ destX = gobDestX;
+ destY = gobDestY;
}
+
}
- obj->goblinX = gobX;
- obj->goblinY = gobY;
- obj->gobDestX = gobDestX;
- obj->gobDestY = gobDestY;
- obj->destX = destX;
- obj->destY = destY;
+ obj->goblinX = gobX;
+ obj->goblinY = gobY;
+ obj->destX = destX;
+ obj->destY = destY;
+ obj->gobDestX = gobDestX;
+ obj->gobDestY = gobDestY;
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
animData->nextState = 1;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
@@ -224,28 +242,29 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
(animData->curLookDir == 2) ? 2 : rotateState(animData->curLookDir, 2);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
- if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
- if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
- animData->nextState = 42;
- else
- animData->nextState = 2;
- } else
+ if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) == 10)
animData->nextState = 40;
- } else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
+ else if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
+ animData->nextState = 42;
+ else
+ animData->nextState = 2;
+ }
+
+ if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 38;
- else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
+ if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
animData->nextState = 26;
}
break;
- case Map::kDirNE:
- animData->nextState = 3;
- if (_vm->_map->_screenWidth == 640) {
+ case kDirNE:
+ animData->nextState = 3;
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
@@ -253,17 +272,17 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirW:
+ case kDirW:
animData->nextState = rotateState(animData->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
animData->nextState = rotateState(animData->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
animData->nextState = 7;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10)
@@ -271,10 +290,10 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
(animData->curLookDir == 6) ? 6 : rotateState(animData->curLookDir, 6);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 39;
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
@@ -282,9 +301,9 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirSE:
+ case kDirSE:
animData->nextState = 5;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) != 10)
@@ -293,7 +312,7 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
break;
default:
- if (animData->curLookDir == 0)
+ if (animData->curLookDir == 0)
animData->nextState = 8;
else if (animData->curLookDir == 2)
animData->nextState = 29;
@@ -307,12 +326,6 @@ void Goblin_v2::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
int16 nextAct, int16 framesCount) {
- Mult::Mult_AnimData *animData;
- int16 gobX;
- int16 gobY;
- int16 animation;
- int16 state;
- int16 layer;
if (!obj->goblinStates)
return;
@@ -320,7 +333,7 @@ void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
movePathFind(obj, 0, 0);
playSounds(obj);
- animData = obj->pAnimData;
+ Mult::Mult_AnimData *animData = obj->pAnimData;
framesCount = _vm->_scenery->getAnimLayer(animData->animation, animData->layer)->framesCount;
@@ -395,72 +408,87 @@ void Goblin_v2::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
}
if ((animData->newState != -1) && (animData->frame == framesCount) &&
- (animData->newState != animData->state)) {
+ (animData->newState != animData->state)) {
+
animData->nextState = animData->newState;
- animData->newState = -1;
- animData->state = animData->nextState;
+ animData->newState = -1;
+ animData->state = animData->nextState;
Scenery::AnimLayer *animLayer =
_vm->_scenery->getAnimLayer(animData->animation, animData->layer);
+
*obj->pPosX += animLayer->animDeltaX;
*obj->pPosY += animLayer->animDeltaY;
- animation = obj->goblinStates[animData->nextState][0].animation;
- layer = obj->goblinStates[animData->nextState][0].layer;
- animData->layer = layer;
+ int16 animation = obj->goblinStates[animData->nextState][0].animation;
+ int16 layer = obj->goblinStates[animData->nextState][0].layer;
+
+ animData->layer = layer;
animData->animation = animation;
- animData->frame = 0;
- } else {
- if (isMovement(animData->state)) {
- state = animData->nextState;
- if (animData->frame == ((framesCount + 1) / 2)) {
- gobX = obj->goblinX;
- gobY = obj->goblinY;
-
- advMovement(obj, state);
-
- if (animData->state != state) {
- animation = obj->goblinStates[state][0].animation;
- layer = obj->goblinStates[state][0].layer;
- animData->layer = layer;
- animData->animation = animation;
- animData->frame = 0;
- animData->state = state;
- _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
- else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
- }
- }
- }
+ animData->frame = 0;
- if (animData->frame >= framesCount) {
- state = animData->nextState;
- animation = obj->goblinStates[state][0].animation;
- layer = obj->goblinStates[state][0].layer;
- animData->layer = layer;
- animData->animation = animation;
- animData->frame = 0;
- animData->state = state;
- gobX = obj->goblinX;
- gobY = obj->goblinY;
+ return;
+ }
+
+ if (isMovement(animData->state)) {
+ int16 state = animData->nextState;
+
+ if (animData->frame == ((framesCount + 1) / 2)) {
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY + 1;
advMovement(obj, state);
- _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
- else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ if (animData->state != state) {
+ int16 animation = obj->goblinStates[state][0].animation;
+ int16 layer = obj->goblinStates[state][0].layer;
+
+ animData->layer = layer;
+ animData->animation = animation;
+ animData->frame = 0;
+ animData->state = state;
+
+ _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
+ uint32 gobPosX = gobX * _vm->_map->getTilesWidth();
+ uint32 gobPosY = (gobY * _vm->_map->getTilesHeight()) -
+ (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
+
+ if (_vm->_map->hasBigTiles())
+ gobPosY -= gobY / 2;
+
+ *obj->pPosX = gobPosX;
+ *obj->pPosY = gobPosY;
+ }
}
}
+
+ if (animData->frame < framesCount)
+ return;
+
+ int16 state = animData->nextState;
+ int16 animation = obj->goblinStates[state][0].animation;
+ int16 layer = obj->goblinStates[state][0].layer;
+
+ animData->layer = layer;
+ animData->animation = animation;
+ animData->frame = 0;
+ animData->state = state;
+
+ int16 gobX = obj->goblinX;
+ int16 gobY = obj->goblinY + 1;
+
+ advMovement(obj, state);
+
+ _vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
+ uint32 gobPosX = gobX * _vm->_map->getTilesWidth();
+ uint32 gobPosY = (gobY * _vm->_map->getTilesHeight()) -
+ (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
+
+ if (_vm->_map->hasBigTiles())
+ gobPosY -= gobY / 2;
+
+ *obj->pPosX = gobPosX;
+ *obj->pPosY = gobPosY;
}
void Goblin_v2::handleGoblins() {
diff --git a/engines/gob/goblin_v4.cpp b/engines/gob/goblin_v4.cpp
index 25c52cef35..523357aab1 100644
--- a/engines/gob/goblin_v4.cpp
+++ b/engines/gob/goblin_v4.cpp
@@ -77,8 +77,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
if ((gobX == destX) && (gobY == destY)) {
if (obj->nearestWayPoint > obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -87,8 +91,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->nearestWayPoint--;
} else if (obj->nearestWayPoint < obj->nearestDest) {
_vm->_map->optimizePoints(obj, gobX, gobY);
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
if (_vm->_map->checkDirectPath(obj, gobX, gobY, destX, destY) == 3) {
WRITE_VAR(56, 1);
animData->pathExistence = 0;
@@ -98,8 +106,12 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
} else {
if ((_vm->_map->checkDirectPath(obj, gobX, gobY, gobDestX, gobDestY) == 3) &&
(_vm->_map->getPass(gobDestX, gobDestY) != 0)) {
- destX = _vm->_map->_wayPoints[obj->nearestWayPoint].x;
- destY = _vm->_map->_wayPoints[obj->nearestWayPoint].y;
+
+ const WayPoint &wayPoint = _vm->_map->getWayPoint(obj->nearestWayPoint);
+
+ destX = wayPoint.x;
+ destY = wayPoint.y;
+
WRITE_VAR(56, 1);
} else {
animData->pathExistence = 1;
@@ -123,20 +135,20 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
obj->destX = destX;
obj->destY = destY;
- if (_vm->_map->_widthByte == 4) {
+ if (_vm->_map->getVersion() == 4) {
switch (dir) {
- case Map::kDirNW:
- animData->nextState = turnState(animData->state, Map::kDirNW);
+ case kDirNW:
+ animData->nextState = turnState(animData->state, kDirNW);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 1))
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
- animData->nextState = turnState(animData->state, Map::kDirNW);
+ animData->nextState = turnState(animData->state, kDirNW);
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
- (animData->curLookDir == 2) ? 2 : turnState(animData->state, Map::kDirN);
+ (animData->curLookDir == 2) ? 2 : turnState(animData->state, kDirN);
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
@@ -154,35 +166,35 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
animData->nextState = 26;
break;
- case Map::kDirNE:
- animData->nextState = turnState(animData->state, Map::kDirNE);
+ case kDirNE:
+ animData->nextState = turnState(animData->state, kDirNE);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 3))
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
- animData->nextState = turnState(animData->state, Map::kDirNE);
+ animData->nextState = turnState(animData->state, kDirNE);
break;
- case Map::kDirW:
- animData->nextState = turnState(animData->state, Map::kDirW);
+ case kDirW:
+ animData->nextState = turnState(animData->state, kDirW);
break;
- case Map::kDirE:
- animData->nextState = turnState(animData->state, Map::kDirE);
+ case kDirE:
+ animData->nextState = turnState(animData->state, kDirE);
break;
- case Map::kDirSW:
- animData->nextState = turnState(animData->state, Map::kDirSW);
+ case kDirSW:
+ animData->nextState = turnState(animData->state, kDirSW);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 7))
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY) != 10)
- animData->nextState = turnState(animData->state, Map::kDirSW);
+ animData->nextState = turnState(animData->state, kDirSW);
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
- (animData->curLookDir == 6) ? 6 : turnState(animData->state, Map::kDirS);
+ (animData->curLookDir == 6) ? 6 : turnState(animData->state, kDirS);
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) == 10)
@@ -201,13 +213,13 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
animData->nextState = 27;
break;
- case Map::kDirSE:
- animData->nextState = turnState(animData->state, Map::kDirSE);
+ case kDirSE:
+ animData->nextState = turnState(animData->state, kDirSE);
if ((_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) &&
(animData->nextState == 5))
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY) != 10)
- animData->nextState = turnState(animData->state, Map::kDirSE);
+ animData->nextState = turnState(animData->state, kDirSE);
break;
default:
@@ -260,9 +272,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
} else {
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
animData->nextState = 1;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 40;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10)
@@ -270,10 +282,10 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirN:
+ case kDirN:
animData->nextState =
(animData->curLookDir == 2) ? 2 : rotateState(animData->curLookDir, 2);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10) {
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY - 2) != 10) {
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) == 10)
@@ -289,9 +301,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirNE:
+ case kDirNE:
animData->nextState = 3;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 42;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY - 2) != 10)
@@ -299,17 +311,17 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirW:
+ case kDirW:
animData->nextState = rotateState(animData->curLookDir, 0);
break;
- case Map::kDirE:
+ case kDirE:
animData->nextState = rotateState(animData->curLookDir, 4);
break;
- case Map::kDirSW:
+ case kDirSW:
animData->nextState = 7;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 41;
if (_vm->_map->getPass(obj->goblinX - 1, obj->goblinY + 2) != 10)
@@ -317,10 +329,10 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirS:
+ case kDirS:
animData->nextState =
(animData->curLookDir == 6) ? 6 : rotateState(animData->curLookDir, 6);
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 20)
animData->nextState = 39;
else if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 19)
@@ -328,9 +340,9 @@ void Goblin_v4::movePathFind(Mult::Mult_Object *obj, Gob_Object *gobDesc, int16
}
break;
- case Map::kDirSE:
+ case kDirSE:
animData->nextState = 5;
- if (_vm->_map->_screenWidth == 640) {
+ if (_vm->_map->getScreenWidth() == 640) {
if (_vm->_map->getPass(obj->goblinX, obj->goblinY) == 10)
animData->nextState = 43;
if (_vm->_map->getPass(obj->goblinX + 1, obj->goblinY + 2) != 10)
@@ -496,13 +508,13 @@ void Goblin_v4::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
animData->frame = 0;
animData->state = state;
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ *obj->pPosX = gobX * _vm->_map->getTilesWidth();
}
}
}
@@ -521,13 +533,13 @@ void Goblin_v4::moveAdvance(Mult::Mult_Object *obj, Gob_Object *gobDesc,
advMovement(obj, state);
_vm->_scenery->updateAnim(layer, 0, animation, 0, *obj->pPosX, *obj->pPosY, 0);
- if (_vm->_map->_bigTiles)
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) - (gobY + 1) / 2;
else
- *obj->pPosY = ((gobY + 1) * _vm->_map->_tilesHeight) -
+ *obj->pPosY = ((gobY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *obj->pPosX = gobX * _vm->_map->_tilesWidth;
+ *obj->pPosX = gobX * _vm->_map->getTilesWidth();
}
}
}
@@ -589,35 +601,35 @@ int16 Goblin_v4::turnState(int16 state, uint16 dir) {
}
switch (dir) {
- case Map::kDirNW:
+ case kDirNW:
cx = 1;
break;
- case Map::kDirN:
+ case kDirN:
cx = 2;
break;
- case Map::kDirNE:
+ case kDirNE:
cx = 3;
break;
- case Map::kDirW:
+ case kDirW:
cx = 0;
break;
- case Map::kDirE:
+ case kDirE:
cx = 4;
break;
- case Map::kDirSW:
+ case kDirSW:
cx = 7;
break;
- case Map::kDirS:
+ case kDirS:
cx = 6;
break;
- case Map::kDirSE:
+ case kDirSE:
cx = 5;
break;
}
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index 78b3553aeb..94c3c6fb24 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -23,9 +23,10 @@
*
*/
+#include "common/str.h"
+
#include "gob/hotspots.h"
#include "gob/global.h"
-#include "gob/helper.h"
#include "gob/draw.h"
#include "gob/game.h"
#include "gob/script.h"
@@ -880,10 +881,10 @@ uint16 Hotspots::updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 heig
while (1) {
// If we the edit field has enough space, add a space for the new character
- strncpy0(tempStr, str, 254);
+ Common::strlcpy(tempStr, str, 255);
strcat(tempStr, " ");
if ((editSize != 0) && strlen(tempStr) > editSize)
- strncpy0(tempStr, str, 255);
+ Common::strlcpy(tempStr, str, 256);
// Clear input area
fillRect(xPos, yPos,
@@ -1236,18 +1237,18 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
_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);
+ _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;
}
@@ -1295,7 +1296,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) {
@@ -1392,7 +1393,7 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
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);
+ warning("evaluateNew: Warning, use of type 2 or 20. flags = %d, should be %d", flags, flags&3);
funcEnter = 0;
@@ -1516,7 +1517,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);
@@ -1642,7 +1643,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) {
@@ -2006,14 +2007,14 @@ void Hotspots::checkStringMatch(const Hotspot &spot, const InputDesc &input,
char tempStr[256];
char spotStr[256];
- strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+ Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
if (spot.getType() < kTypeInput3NoLeave)
_vm->_util->cleanupStr(tempStr);
uint16 pos = 0;
do {
- strncpy0(spotStr, str, 255);
+ Common::strlcpy(spotStr, str, 256);
pos += strlen(str) + 1;
str += strlen(str) + 1;
@@ -2106,7 +2107,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 {
@@ -2140,7 +2141,7 @@ void Hotspots::updateAllTexts(const InputDesc *inputs) const {
// Get its text
char tempStr[256];
- strncpy0(tempStr, GET_VARO_STR(spot.key), 255);
+ Common::strlcpy(tempStr, GET_VARO_STR(spot.key), 256);
// Coordinates
uint16 x = spot.left;
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_v3.cpp b/engines/gob/init_v3.cpp
index cb4a0b713f..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 f6e6d41100..8db42b217c 100644
--- a/engines/gob/inter.cpp
+++ b/engines/gob/inter.cpp
@@ -268,23 +268,23 @@ void Inter::funcBlock(int16 retFlag) {
int addr = _vm->_game->_script->pos();
if ((startaddr == 0x18B4 && addr == 0x1A7F && // Zombie, EGA
- !strncmp(_vm->_game->_curTotFile, "avt005.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt005.tot")) ||
(startaddr == 0x188D && addr == 0x1A58 && // Zombie, Mac
- !strncmp(_vm->_game->_curTotFile, "avt005.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt005.tot")) ||
(startaddr == 0x1299 && addr == 0x139A && // Dungeon
- !strncmp(_vm->_game->_curTotFile, "avt006.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt006.tot")) ||
(startaddr == 0x11C0 && addr == 0x12C9 && // Cauldron, EGA
- !strncmp(_vm->_game->_curTotFile, "avt012.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt012.tot")) ||
(startaddr == 0x11C8 && addr == 0x1341 && // Cauldron, Mac
- !strncmp(_vm->_game->_curTotFile, "avt012.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt012.tot")) ||
(startaddr == 0x09F2 && addr == 0x0AF3 && // Statue
- !strncmp(_vm->_game->_curTotFile, "avt016.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt016.tot")) ||
(startaddr == 0x0B92 && addr == 0x0C93 && // Castle
- !strncmp(_vm->_game->_curTotFile, "avt019.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt019.tot")) ||
(startaddr == 0x17D9 && addr == 0x18DA && // Finale, EGA
- !strncmp(_vm->_game->_curTotFile, "avt022.tot", 10)) ||
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt022.tot")) ||
(startaddr == 0x17E9 && addr == 0x19A8 && // Finale, Mac
- !strncmp(_vm->_game->_curTotFile, "avt022.tot", 10))) {
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt022.tot"))) {
_vm->_util->longDelay(5000);
}
@@ -295,7 +295,7 @@ void Inter::funcBlock(int16 retFlag) {
// 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)) {
+ !scumm_stricmp(_vm->_game->_curTotFile, "PLANQUE.tot")) {
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
@@ -306,6 +306,21 @@ void Inter::funcBlock(int16 retFlag) {
} // End of workaround
cmd = _vm->_game->_script->readByte();
+
+ // WORKAROUND:
+ // A VGA version has some broken code in its scripts, this workaround skips the corrupted parts.
+ if (_vm->getGameType() == kGameTypeFascination) {
+ int addr = _vm->_game->_script->pos();
+ if ((startaddr == 0x212D) && (addr == 0x290E) && (cmd == 0x90) && !scumm_stricmp(_vm->_game->_curTotFile, "INTRO1.tot")) {
+ _vm->_game->_script->skip(2);
+ cmd = _vm->_game->_script->readByte();
+ }
+ if ((startaddr == 0x207D) && (addr == 0x22CE) && (cmd == 0x90) && !scumm_stricmp(_vm->_game->_curTotFile, "INTRO2.tot")) {
+ _vm->_game->_script->skip(2);
+ cmd = _vm->_game->_script->readByte();
+ }
+ }
+
if ((cmd >> 4) >= 12) {
cmd2 = 16 - (cmd >> 4);
cmd &= 0xF;
diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index ca97483054..4554a0783b 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -542,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 d2b97eea43..895eb85440 100644
--- a/engines/gob/inter_fascin.cpp
+++ b/engines/gob/inter_fascin.cpp
@@ -137,7 +137,8 @@ bool Inter_Fascination::oFascin_repeatUntil(OpFuncParams &params) {
// 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 == 3519)) ||
+ (!scumm_stricmp(_vm->_game->_curTotFile, "INTRO2.TOT") && (blockPos == 3265))) //PC Hebrew
_terminate = 1;
}
} while (!flag && !_break && !_terminate && !_vm->shouldQuit());
diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp
index befed4b1c2..050bbce132 100644
--- a/engines/gob/inter_playtoons.cpp
+++ b/engines/gob/inter_playtoons.cpp
@@ -24,12 +24,12 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gui/message.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -415,9 +415,9 @@ void Inter_Playtoons::oPlaytoons_copyFile() {
char fileName2[128];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName1, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName1, _vm->_game->_script->getResultStr(), 128);
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName2, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName2, _vm->_game->_script->getResultStr(), 128);
warning("Playtoons Stub: copy file from \"%s\" to \"%s\"", fileName1, fileName2);
}
@@ -427,7 +427,7 @@ void Inter_Playtoons::oPlaytoons_openItk() {
char *backSlash;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 124);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 124);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 11fe0c9c5e..6dab8161d0 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -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);
@@ -972,7 +971,7 @@ bool Inter_v1::o1_loadTot(OpFuncParams &params) {
if ((_vm->_game->_script->peekByte() & 0x80) != 0) {
_vm->_game->_script->skip(1);
_vm->_game->_script->evalExpr(0);
- strncpy0(buf, _vm->_game->_script->getResultStr(), 15);
+ Common::strlcpy(buf, _vm->_game->_script->getResultStr(), 16);
} else {
size = _vm->_game->_script->readInt8();
memcpy(buf, _vm->_game->_script->readString(size), size);
@@ -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();
@@ -1510,7 +1512,7 @@ bool Inter_v1::o1_strToLong(OpFuncParams &params) {
int32 res;
strVar = _vm->_game->_script->readVarIndex();
- strncpy0(str, GET_VARO_STR(strVar), 19);
+ Common::strlcpy(str, GET_VARO_STR(strVar), 20);
res = atoi(str);
destVar = _vm->_game->_script->readVarIndex();
@@ -2560,8 +2562,8 @@ void Inter_v1::animPalette() {
}
void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
- for (int y = 0; y < _vm->_map->_mapHeight; y++) {
- for (int x = 0; x < _vm->_map->_mapWidth; x++) {
+ for (int y = 0; y < _vm->_map->getMapHeight(); y++) {
+ for (int x = 0; x < _vm->_map->getMapWidth(); x++) {
if ((_vm->_map->getItem(x, y) & 0xFF) == item)
_vm->_map->setItem(x, y, _vm->_map->getItem(x, y) & 0xFF00);
else if (((_vm->_map->getItem(x, y) & 0xFF00) >> 8) == item)
@@ -2569,7 +2571,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
}
}
- if (xPos < _vm->_map->_mapWidth - 1) {
+ if (xPos < _vm->_map->getMapWidth() - 1) {
if (yPos > 0) {
if (((_vm->_map->getItem(xPos, yPos) & 0xFF00) != 0) ||
((_vm->_map->getItem(xPos, yPos - 1) & 0xFF00) != 0) ||
@@ -2658,7 +2660,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
return;
}
- if ((xPos < _vm->_map->_mapWidth - 2) &&
+ if ((xPos < _vm->_map->getMapWidth() - 2) &&
(_vm->_map->getPass(xPos + 2, yPos) == 1)) {
_vm->_map->_itemPoses[item].x = xPos + 2;
_vm->_map->_itemPoses[item].y = yPos;
@@ -2666,7 +2668,7 @@ void Inter_v1::manipulateMap(int16 xPos, int16 yPos, int16 item) {
return;
}
- if ((xPos < _vm->_map->_mapWidth - 1) &&
+ if ((xPos < _vm->_map->getMapWidth() - 1) &&
(_vm->_map->getPass(xPos + 1, yPos) == 1)) {
_vm->_map->_itemPoses[item].x = xPos + 1;
_vm->_map->_itemPoses[item].y = yPos;
diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp
index 0003332e47..2cb430ff9b 100644
--- a/engines/gob/inter_v2.cpp
+++ b/engines/gob/inter_v2.cpp
@@ -24,6 +24,7 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gui/message.h"
@@ -32,7 +33,6 @@
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
@@ -432,7 +432,7 @@ void Inter_v2::o2_loadMultObject() {
obj.gobDestY = val;
obj.goblinY = val;
- *(obj.pPosX) *= _vm->_map->_tilesWidth;
+ *(obj.pPosX) *= _vm->_map->getTilesWidth();
layer = objAnim.layer;
animation = obj.goblinStates[layer][0].animation;
@@ -447,14 +447,14 @@ void Inter_v2::o2_loadMultObject() {
_vm->_scenery->updateAnim(layer, 0, animation, 0,
*(obj.pPosX), *(obj.pPosY), 0);
- if (!_vm->_map->_bigTiles)
- *(obj.pPosY) = (obj.goblinY + 1) * _vm->_map->_tilesHeight
+ if (!_vm->_map->hasBigTiles())
+ *(obj.pPosY) = (obj.goblinY + 1) * _vm->_map->getTilesHeight()
- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
else
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
((obj.goblinY + 1) / 2);
- *(obj.pPosX) = obj.goblinX * _vm->_map->_tilesWidth;
+ *(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
} else if ((objAnim.animType == 101) && (objIndex < _vm->_goblin->_gobsCount)) {
@@ -530,7 +530,7 @@ void Inter_v2::o2_readLIC() {
char path[40];
_vm->_game->_script->evalExpr(0);
- strncpy0(path, _vm->_game->_script->getResultStr(), 35);
+ Common::strlcpy(path, _vm->_game->_script->getResultStr(), 36);
strcat(path, ".LIC");
_vm->_sound->cdLoadLIC(path);
@@ -778,14 +778,14 @@ void Inter_v2::o2_setGoblinState() {
_vm->_scenery->updateAnim(layer, 0, animation, 0,
*(obj.pPosX), *(obj.pPosY), 0);
- if (_vm->_map->_bigTiles)
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ if (_vm->_map->hasBigTiles())
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
((obj.goblinY + 1) / 2);
else
- *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->_tilesHeight) -
+ *(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
- *(obj.pPosX) = obj.goblinX * _vm->_map->_tilesWidth;
+ *(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
break;
}
}
@@ -963,7 +963,7 @@ void Inter_v2::o2_playImd() {
_vm->_game->_script->evalExpr(0);
_vm->_game->_script->getResultStr()[8] = 0;
- strncpy0(imd, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(imd, _vm->_game->_script->getResultStr(), 128);
VideoPlayer::Properties props;
@@ -1031,7 +1031,7 @@ void Inter_v2::o2_openItk() {
char fileName[32];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 27);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 28);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
@@ -1462,7 +1462,7 @@ void Inter_v2::o2_loadInfogramesIns(OpGobParams &params) {
varName = _vm->_game->_script->readInt16();
- strncpy0(fileName, GET_VAR_STR(varName), 15);
+ Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
strcat(fileName, ".INS");
_vm->_sound->infogramesLoadInstruments(fileName);
@@ -1474,7 +1474,7 @@ void Inter_v2::o2_playInfogrames(OpGobParams &params) {
varName = _vm->_game->_script->readInt16();
- strncpy0(fileName, GET_VAR_STR(varName), 15);
+ Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
strcat(fileName, ".DUM");
_vm->_sound->infogramesLoadSong(fileName);
@@ -1560,7 +1560,7 @@ int16 Inter_v2::loadSound(int16 search) {
if (id == -1) {
char sndfile[14];
- strncpy0(sndfile, _vm->_game->_script->readString(9), 9);
+ Common::strlcpy(sndfile, _vm->_game->_script->readString(9), 10);
if (type == SOUND_ADL)
strcat(sndfile, ".ADL");
diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp
index ce1f19e9a7..698dddeae9 100644
--- a/engines/gob/inter_v4.cpp
+++ b/engines/gob/inter_v4.cpp
@@ -24,11 +24,11 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/draw.h"
#include "gob/game.h"
@@ -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];
}
}
@@ -145,7 +145,7 @@ void Inter_v4::o4_playVmdOrMusic() {
bool close;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 128);
// WORKAROUND: The nut rolling animation in the administration center
// in Woodruff is called "noixroul", but the scripts think it's "noixroule".
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 a5a4f7d666..e1eff279b5 100644
--- a/engines/gob/inter_v6.cpp
+++ b/engines/gob/inter_v6.cpp
@@ -24,12 +24,12 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/file.h"
#include "graphics/dither.h"
#include "gob/gob.h"
#include "gob/inter.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/game.h"
@@ -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);
}
@@ -105,7 +104,7 @@ void Inter_v6::o6_playVmdOrMusic() {
bool close;
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 127);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 128);
VideoPlayer::Properties props;
@@ -188,7 +187,7 @@ void Inter_v6::o6_openItk() {
char fileName[32];
_vm->_game->_script->evalExpr(0);
- strncpy0(fileName, _vm->_game->_script->getResultStr(), 27);
+ Common::strlcpy(fileName, _vm->_game->_script->getResultStr(), 28);
if (!strchr(fileName, '.'))
strcat(fileName, ".ITK");
@@ -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/map.cpp b/engines/gob/map.cpp
index 9f1f5bdc59..500f6515ec 100644
--- a/engines/gob/map.cpp
+++ b/engines/gob/map.cpp
@@ -32,33 +32,40 @@
namespace Gob {
Map::Map(GobEngine *vm) : _vm(vm) {
- _widthByte = 0;
- _mapWidth = -1;
+ _mapVersion = 0;
+
+ _passWidth = 0;
+ _mapWidth = -1;
_mapHeight = -1;
- _screenWidth = 0;
+ _passMap = 0;
+
+ _screenWidth = 0;
_screenHeight = 0;
- _tilesWidth = 0;
+
+ _tilesWidth = 0;
_tilesHeight = 0;
- _passWidth = 0;
- _passMap = 0;
- _itemsMap = 0;
- _wayPointsCount = 0;
- _wayPoints = 0;
_bigTiles = false;
+ _wayPointCount = 0;
+ _wayPoints = 0;
+
+ _nearestWayPoint = 0;
+ _nearestDest = 0;
+
+ _itemsMap = 0;
+
for (int i = 0; i < 40; i++) {
- _itemPoses[i].x = 0;
- _itemPoses[i].y = 0;
+ _itemPoses[i].x = 0;
+ _itemPoses[i].y = 0;
_itemPoses[i].orient = 0;
}
- _nearestWayPoint = 0;
- _nearestDest = 0;
_curGoblinX = 0;
_curGoblinY = 0;
_destX = 0;
_destY = 0;
+
_sourceFile[0] = 0;
_loadFromAvo = false;
@@ -76,6 +83,87 @@ Map::~Map() {
delete[] _wayPoints;
}
+uint8 Map::getVersion() const {
+ return _mapVersion;
+}
+
+int16 Map::getMapWidth() const {
+ return _mapWidth;
+}
+
+int16 Map::getMapHeight() const {
+ return _mapHeight;
+}
+
+int16 Map::getScreenWidth() const {
+ return _screenWidth;
+}
+
+int16 Map::getScreenHeight() const {
+ return _screenHeight;
+}
+
+int16 Map::getTilesWidth() const {
+ return _tilesWidth;
+}
+
+int16 Map::getTilesHeight() const {
+ return _tilesHeight;
+}
+
+bool Map::hasBigTiles() const {
+ return _bigTiles;
+}
+
+int8 Map::getPass(int x, int y, int width) const {
+ if (!_passMap)
+ return 0;
+
+ if ((x < 0) || (y < 0) || (x >= _mapWidth) || (y >= _mapHeight))
+ return 0;
+
+ if (width == -1)
+ width = _passWidth;
+ return _passMap[y * width + x];
+}
+
+void Map::setPass(int x, int y, int8 pass, int width) {
+ if (!_passMap)
+ return;
+
+ if ((x < 0) || (y < 0) || (x >= _mapWidth) || (y >= _mapHeight))
+ return;
+
+ if (width == -1)
+ width = _passWidth;
+ _passMap[y * width + x] = pass;
+}
+
+const WayPoint &Map::getWayPoint(int n) const {
+ assert(_wayPoints);
+ assert(n < _wayPointCount);
+
+ return _wayPoints[n];
+}
+
+int16 Map::getItem(int x, int y) const {
+ assert(_itemsMap);
+
+ x = CLIP<int>(x, 0, _mapWidth - 1);
+ y = CLIP<int>(y, 0, _mapHeight - 1);
+
+ return _itemsMap[y][x];
+}
+
+void Map::setItem(int x, int y, int16 item) {
+ assert(_itemsMap);
+
+ x = CLIP<int>(x, 0, _mapWidth - 1);
+ y = CLIP<int>(y, 0, _mapHeight - 1);
+
+ _itemsMap[y][x] = item;
+}
+
void Map::placeItem(int16 x, int16 y, int16 id) {
if ((getItem(x, y) & 0xFF00) != 0)
setItem(x, y, (getItem(x, y) & 0xFF00) | id);
@@ -83,150 +171,175 @@ void Map::placeItem(int16 x, int16 y, int16 id) {
setItem(x, y, (getItem(x, y) & 0x00FF) | (id << 8));
}
-enum {
- kLeft = (1 << 0),
- kUp = (1 << 1),
- kRight = (1 << 2),
- kDown = (1 << 3)
-};
-
-int16 Map::getDirection(int16 x0, int16 y0, int16 x1, int16 y1) {
- int16 dir = 0;
-
+Direction Map::getDirection(int16 x0, int16 y0, int16 x1, int16 y1) {
if ((x0 == x1) && (y0 == y1))
- return 0;
+ // Already at the destination
+ return kDirNone;
if ((x1 < 0) || (x1 > _mapWidth) || (y1 < 0) || (y1 > _mapHeight))
- return 0;
+ // Destination out of range
+ return kDirNone;
+ RelativeDirection relDir = kRelDirNone;
+
+ // Find the direct direction we want to move
if (y1 > y0)
- dir |= kDown;
+ relDir = kRelDirDown;
else if (y1 < y0)
- dir |= kUp;
+ relDir = kRelDirUp;
if (x1 > x0)
- dir |= kRight;
+ relDir = (RelativeDirection)(relDir | kRelDirRight);
else if (x1 < x0)
- dir |= kLeft;
+ relDir = (RelativeDirection)(relDir | kRelDirLeft);
- if ((getPass(x0, y0) == 3) && (dir & kUp)) {
- if ((getPass(x0, y0 - 1) != 0))
- return kDirN;
- }
- if ((getPass(x0, y0) == 3) && (dir & kDown)) {
- if ((getPass(x0, y0 + 1) != 0))
- return kDirS;
- }
+ // Are we on ladders and can continue the ladder in the wanted direction?
+ if ((getPass(x0, y0) == 3) && (relDir & kRelDirUp ) && (getPass(x0, y0 - 1) != 0))
+ return kDirN;
- if ((getPass(x0, y0) == 6) && (dir & kUp)) {
- if ((getPass(x0, y0 - 1) != 0))
- return kDirN;
- }
+ if ((getPass(x0, y0) == 3) && (relDir & kRelDirDown) && (getPass(x0, y0 + 1) != 0))
+ return kDirS;
- if ((getPass(x0, y0) == 6) && (dir & kDown)) {
- if ((getPass(x0, y0 + 1) != 0))
- return kDirS;
- }
+ if ((getPass(x0, y0) == 6) && (relDir & kRelDirUp ) && (getPass(x0, y0 - 1) != 0))
+ return kDirN;
- if (dir == kLeft) {
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if ((getPass(x0, y0) == 6) && (relDir & kRelDirDown) && (getPass(x0, y0 + 1) != 0))
+ return kDirS;
+
+
+ // Want to go left
+ if (relDir == kRelDirLeft) {
+ if (getPass(x0 - 1, y0) != 0)
+ // Can go west
return kDirW;
- return 0;
+
+ // Can't go
+ return kDirNone;
}
- if (dir == kRight) {
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ // Want to go left
+ if (relDir == kRelDirRight) {
+ if (getPass(x0 + 1, y0) != 0)
+ // Can go east
return kDirE;
- return 0;
+
+ // Can't go
+ return kDirNone;
}
- if (dir == kUp) {
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+
+ // Want to go up
+ if (relDir == kRelDirUp) {
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can go north
return kDirN;
- if (((y0 - 1) >= 0) && ((x0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 - 1) != 0))
+ if (getPass(x0 - 1, y0 - 1) != 0)
+ // Can up north-west instead
return kDirNW;
- if (((y0 - 1) >= 0) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 - 1) != 0))
+ if (getPass(x0 + 1, y0 - 1) != 0)
+ // Can up north-east instead
return kDirNE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == kDown) {
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ // Want to go down
+ if (relDir == kRelDirDown) {
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can go south
return kDirS;
- if (((y0 + 1) < _mapHeight) && ((x0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 + 1) != 0))
+ if (getPass(x0 - 1, y0 + 1) != 0)
+ // Can up south-west instead
return kDirSW;
- if (((y0 + 1) < _mapHeight) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 + 1) != 0))
+ if (getPass(x0 + 1, y0 + 1) != 0)
+ // Can up south-east instead
return kDirSE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kRight | kUp)) {
- if (((y0 - 1) >= 0) && ((x0 + 1) < _mapWidth) &&
- (getPass(x0 + 1, y0 - 1) != 0))
+
+ // Want to go up and right
+ if (relDir == kRelDirRightUp) {
+ if (getPass(x0 + 1, y0 - 1) != 0)
+ // Can go north-east
return kDirNE;
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can only go north
return kDirN;
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ if (getPass(x0 + 1, y0 ) != 0)
+ // Can only go east
return kDirE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kRight | kDown)) {
- if (((x0 + 1) < _mapWidth) && ((y0 + 1) < _mapHeight) &&
- (getPass(x0 + 1, y0 + 1) != 0))
+ // Want to go down and right
+ if (relDir == kRelDirRightDown) {
+ if (getPass(x0 + 1, y0 + 1) != 0)
+ // Can go south-east
return kDirSE;
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can only go south
return kDirS;
- if (((x0 + 1) < _mapWidth) && (getPass(x0 + 1, y0) != 0))
+ if (getPass(x0 + 1, y0 ) != 0)
+ // Can only go east
return kDirE;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kLeft | kUp)) {
- if (((x0 - 1) >= 0) && ((y0 - 1) >= 0) &&
- (getPass(x0 - 1, y0 - 1) != 0))
+ // Want to go up and left
+ if (relDir == kRelDirLeftUp) {
+ if (getPass(x0 - 1, y0 - 1) != 0)
+ // Can go north-west
return kDirNW;
- if (((y0 - 1) >= 0) && (getPass(x0, y0 - 1) != 0))
+ if (getPass(x0 , y0 - 1) != 0)
+ // Can only go north
return kDirN;
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if (getPass(x0 - 1, y0 ) != 0)
+ // Can only go west
return kDirW;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- if (dir == (kLeft | kDown)) {
- if (((x0 - 1) >= 0) && ((y0 + 1) < _mapHeight) &&
- (getPass(x0 - 1, y0 + 1) != 0))
+ // Want to go left and down
+ if (relDir == kRelDirLeftDown) {
+ if (getPass(x0 - 1, y0 + 1) != 0)
+ // Can go south-west
return kDirSW;
- if (((y0 + 1) < _mapHeight) && (getPass(x0, y0 + 1) != 0))
+ if (getPass(x0 , y0 + 1) != 0)
+ // Can only go south
return kDirS;
- if (((x0 - 1) >= 0) && (getPass(x0 - 1, y0) != 0))
+ if (getPass(x0 - 1, y0 ) != 0)
+ // Can only go west
return kDirW;
- return 0;
+ // Can't go at all
+ return kDirNone;
}
- return -1;
+
+ warning("Map::getDirection(): Invalid direction?!?");
+ return kDirNone;
}
int16 Map::findNearestWayPoint(int16 x, int16 y) {
@@ -236,7 +349,7 @@ int16 Map::findNearestWayPoint(int16 x, int16 y) {
length = 30000;
- for (int i = 0; i < _wayPointsCount; i++) {
+ for (int i = 0; i < _wayPointCount; i++) {
if ((_wayPoints[i].x < 0) || (_wayPoints[i].x >= _mapWidth) ||
(_wayPoints[i].y < 0) || (_wayPoints[i].y >= _mapHeight))
break;
@@ -318,73 +431,81 @@ void Map::findNearestWalkable(int16 &gobDestX, int16 &gobDestY,
gobDestY -= distance;
}
-int16 Map::checkDirectPath(Mult::Mult_Object *obj,
- int16 x0, int16 y0, int16 x1, int16 y1) {
- uint16 dir;
+void Map::moveDirection(Direction dir, int16 &x, int16 &y) {
+ switch (dir) {
+ case kDirNW:
+ x--;
+ y--;
+ break;
+
+ case kDirN:
+ y--;
+ break;
+
+ case kDirNE:
+ x++;
+ y--;
+ break;
+
+ case kDirW:
+ x--;
+ break;
+
+ case kDirE:
+ x++;
+ break;
+
+ case kDirSW:
+ x--;
+ y++;
+ break;
+
+ case kDirS:
+ y++;
+ break;
+
+ case kDirSE:
+ x++;
+ y++;
+ break;
+
+ default:
+ break;
+ }
+}
+
+int16 Map::checkDirectPath(Mult::Mult_Object *obj, int16 x0, int16 y0, int16 x1, int16 y1) {
while (1) {
- dir = getDirection(x0, y0, x1, y1);
+ Direction dir = getDirection(x0, y0, x1, y1);
if (obj) {
- if (obj->nearestWayPoint < obj->nearestDest) {
- if (_wayPoints[obj->nearestWayPoint + 1].notWalkable == 1)
- return 3;
- } else if (obj->nearestWayPoint > obj->nearestDest) {
- if (obj->nearestDest > 0)
- if (_wayPoints[obj->nearestDest - 1].notWalkable == 1)
+ // Check for a blocking waypoint
+
+ if (obj->nearestWayPoint < obj->nearestDest)
+ if ((obj->nearestWayPoint + 1) < _wayPointCount)
+ if (_wayPoints[obj->nearestWayPoint + 1].notWalkable == 1)
+ return 3;
+
+ if (obj->nearestWayPoint > obj->nearestDest)
+ if (obj->nearestWayPoint > 0)
+ if (_wayPoints[obj->nearestWayPoint - 1].notWalkable == 1)
return 3;
- }
}
if ((x0 == x1) && (y0 == y1))
+ // Successfully reached the destination
return 1;
- if (dir == 0)
+ if (dir == kDirNone)
+ // No way
return 3;
- switch (dir) {
- case kDirNW:
- x0--;
- y0--;
- break;
-
- case kDirN:
- y0--;
- break;
-
- case kDirNE:
- x0++;
- y0--;
- break;
-
- case kDirW:
- x0--;
- break;
-
- case kDirE:
- x0++;
- break;
-
- case kDirSW:
- x0--;
- y0++;
- break;
-
- case kDirS:
- y0++;
- break;
-
- case kDirSE:
- x0++;
- y0++;
- break;
- }
+ moveDirection(dir, x0, y0);
}
}
-int16 Map::checkLongPath(int16 x0, int16 y0,
- int16 x1, int16 y1, int16 i0, int16 i1) {
- uint16 dir = 0;
+int16 Map::checkLongPath(int16 x0, int16 y0, int16 x1, int16 y1, int16 i0, int16 i1) {
int16 curX = x0;
int16 curY = y0;
int16 nextLink = 1;
@@ -417,47 +538,13 @@ int16 Map::checkLongPath(int16 x0, int16 y0,
return 1;
return 0;
}
- dir = getDirection(x0, y0, curX, curY);
- switch (dir) {
- case 0:
- return 0;
-
- case kDirNW:
- x0--;
- y0--;
- break;
- case kDirN:
- y0--;
- break;
-
- case kDirNE:
- x0++;
- y0--;
- break;
-
- case kDirW:
- x0--;
- break;
-
- case kDirE:
- x0++;
- break;
-
- case kDirSW:
- x0--;
- y0++;
- break;
-
- case kDirS:
- y0++;
- break;
+ Direction dir = getDirection(x0, y0, curX, curY);
+ if (dir == kDirNone)
+ // No way
+ return 0;
- case kDirSE:
- x0++;
- y0++;
- break;
- }
+ moveDirection(dir, x0, y0);
}
}
diff --git a/engines/gob/map.h b/engines/gob/map.h
index 4a63e84a63..4bf2dc6228 100644
--- a/engines/gob/map.h
+++ b/engines/gob/map.h
@@ -30,54 +30,48 @@
namespace Gob {
+enum RelativeDirection {
+ kRelDirNone = 0 ,
+
+ kRelDirLeft = (1 << 0),
+ kRelDirUp = (1 << 1),
+ kRelDirRight = (1 << 2),
+ kRelDirDown = (1 << 3),
+
+ kRelDirLeftUp = kRelDirLeft | kRelDirUp,
+ kRelDirLeftDown = kRelDirLeft | kRelDirDown,
+ kRelDirRightUp = kRelDirRight | kRelDirUp,
+ kRelDirRightDown = kRelDirRight | kRelDirDown
+};
+
// The same numeric values are also used for the arrow keys.
+enum Direction {
+ kDirNone = 0x0000,
+ kDirNW = 0x4700,
+ kDirN = 0x4800,
+ kDirNE = 0x4900,
+ kDirW = 0x4B00,
+ kDirE = 0x4D00,
+ kDirSW = 0x4F00,
+ kDirS = 0x5000,
+ kDirSE = 0x5100
+};
+
+struct WayPoint {
+ int16 x;
+ int16 y;
+ int16 notWalkable;
+};
+
+struct ItemPos {
+ int8 x;
+ int8 y;
+ int8 orient;
+};
+
class Map {
public:
- enum {
- kDirNW = 0x4700,
- kDirN = 0x4800,
- kDirNE = 0x4900,
- kDirW = 0x4B00,
- kDirE = 0x4D00,
- kDirSW = 0x4F00,
- kDirS = 0x5000,
- kDirSE = 0x5100
- };
-
-#include "common/pack-start.h" // START STRUCT PACKING
-
- struct Point {
- int16 x;
- int16 y;
- int16 notWalkable;
- } PACKED_STRUCT;
-
-#define szMap_ItemPos 3
-
- struct ItemPos {
- int8 x;
- int8 y;
- int8 orient;
- } PACKED_STRUCT;
-
-#include "common/pack-end.h" // END STRUCT PACKING
-
- byte _widthByte;
- int16 _mapWidth;
- int16 _mapHeight;
- int16 _screenWidth;
- int16 _screenHeight;
- int16 _tilesWidth;
- int16 _tilesHeight;
- int16 _passWidth;
- bool _bigTiles;
- bool _mapUnknownBool;
-
- int8 *_passMap; // [y * _mapWidth + x], getPass(x, y);
- int16 **_itemsMap; // [y][x]
- int16 _wayPointsCount;
- Point *_wayPoints;
int16 _nearestWayPoint;
int16 _nearestDest;
@@ -89,12 +83,36 @@ public:
ItemPos _itemPoses[40];
char _sourceFile[15];
+ Map(GobEngine *vm);
+ virtual ~Map();
+
+ uint8 getVersion() const;
+
+ int16 getMapWidth() const;
+ int16 getMapHeight() const;
+
+ int16 getScreenWidth() const;
+ int16 getScreenHeight() const;
+
+ int16 getTilesWidth() const;
+ int16 getTilesHeight() const;
+
+ bool hasBigTiles() const;
+
+ int8 getPass(int x, int y, int width = -1) const;
+ void setPass(int x, int y, int8 pass, int width = -1);
+
+ const WayPoint &getWayPoint(int n) const;
+
void findNearestWalkable(int16 &gobDestX, int16 &gobDestY,
int16 mouseX, int16 mouseY);
+ int16 getItem(int x, int y) const;
+ void setItem(int x, int y, int16 item);
void placeItem(int16 x, int16 y, int16 id);
- int16 getDirection(int16 x0, int16 y0, int16 x1, int16 y1);
+ Direction getDirection(int16 x0, int16 y0, int16 x1, int16 y1);
+
int16 checkDirectPath(Mult::Mult_Object *obj, int16 x0,
int16 y0, int16 x1, int16 y1);
int16 checkLongPath(int16 x0, int16 y0,
@@ -102,65 +120,52 @@ public:
void loadMapsInitGobs();
- virtual int16 getItem(int x, int y) = 0;
- virtual void setItem(int x, int y, int16 item) = 0;
-
- virtual int8 getPass(int x, int y, int heightOff = -1) = 0;
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) = 0;
-
virtual void loadMapObjects(const char *avjFile) = 0;
virtual void findNearestToGob(Mult::Mult_Object *obj) = 0;
virtual void findNearestToDest(Mult::Mult_Object *obj) = 0;
virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y) = 0;
- Map(GobEngine *vm);
- virtual ~Map();
-
protected:
+ GobEngine *_vm;
+
bool _loadFromAvo;
- GobEngine *_vm;
+ uint8 _mapVersion;
- int16 findNearestWayPoint(int16 x, int16 y);
-};
+ int16 _mapWidth;
+ int16 _mapHeight;
-class Map_v1 : public Map {
-public:
- virtual void loadMapObjects(const char *avjFile);
- virtual void findNearestToGob(Mult::Mult_Object *obj);
- virtual void findNearestToDest(Mult::Mult_Object *obj);
- virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
+ int16 _screenWidth;
+ int16 _screenHeight;
- virtual int16 getItem(int x, int y) {
- assert(_itemsMap);
+ int16 _tilesWidth;
+ int16 _tilesHeight;
- x = CLIP<int>(x, 0, _mapWidth - 1);
- y = CLIP<int>(y, 0, _mapHeight - 1);
+ bool _bigTiles;
- return _itemsMap[y][x];
- }
- virtual void setItem(int x, int y, int16 item) {
- assert(_itemsMap);
+ bool _mapUnknownBool;
- x = CLIP<int>(x, 0, _mapWidth - 1);
- y = CLIP<int>(y, 0, _mapHeight - 1);
+ int16 _passWidth;
+ int8 *_passMap; // [y * _mapWidth + x], getPass(x, y);
- _itemsMap[y][x] = item;
- }
+ int16 _wayPointCount;
+ WayPoint *_wayPoints;
- virtual int8 getPass(int x, int y, int heightOff = -1) {
- if (!_passMap)
- return 0;
+ int16 **_itemsMap; // [y][x]
- return _passMap[y * _mapWidth + x];
- }
+ int16 findNearestWayPoint(int16 x, int16 y);
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) {
- if (!_passMap)
- return;
+private:
+ // Move the x and y values according to the direction
+ void moveDirection(Direction dir, int16 &x, int16 &y);
+};
- _passMap[y * _mapWidth + x] = pass;
- }
+class Map_v1 : public Map {
+public:
+ virtual void loadMapObjects(const char *avjFile);
+ virtual void findNearestToGob(Mult::Mult_Object *obj);
+ virtual void findNearestToDest(Mult::Mult_Object *obj);
+ virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
Map_v1(GobEngine *vm);
virtual ~Map_v1();
@@ -180,24 +185,6 @@ public:
virtual void findNearestToDest(Mult::Mult_Object *obj);
virtual void optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y);
- virtual int8 getPass(int x, int y, int heightOff = -1) {
- if (!_passMap)
- return 0;
-
- if (heightOff == -1)
- heightOff = _passWidth;
- return _passMap[y * heightOff + x];
- }
-
- virtual void setPass(int x, int y, int8 pass, int heightOff = -1) {
- if (!_passMap)
- return;
-
- if (heightOff == -1)
- heightOff = _passWidth;
- _passMap[y * heightOff + x] = pass;
- }
-
Map_v2(GobEngine *vm);
virtual ~Map_v2();
diff --git a/engines/gob/map_v1.cpp b/engines/gob/map_v1.cpp
index d8898c83d3..aa6603fd55 100644
--- a/engines/gob/map_v1.cpp
+++ b/engines/gob/map_v1.cpp
@@ -44,7 +44,8 @@ void Map_v1::init() {
if (_passMap || _itemsMap)
return;
- _mapWidth = 26;
+ _passWidth = 26;
+ _mapWidth = 26;
_mapHeight = 28;
_passMap = new int8[_mapHeight * _mapWidth];
@@ -56,9 +57,9 @@ void Map_v1::init() {
memset(_itemsMap[i], 0, _mapWidth * sizeof(int16));
}
- _wayPointsCount = 40;
- _wayPoints = new Point[40];
- memset(_wayPoints, 0, sizeof(Point));
+ _wayPointCount = 40;
+ _wayPoints = new WayPoint[40];
+ memset(_wayPoints, 0, sizeof(WayPoint));
}
void Map_v1::loadMapObjects(const char *avjFile) {
@@ -97,7 +98,12 @@ void Map_v1::loadMapObjects(const char *avjFile) {
_wayPoints[i].x = mapData.readUint16LE();
_wayPoints[i].y = mapData.readUint16LE();
}
- mapData.read(_itemPoses, szMap_ItemPos * 20);
+
+ for (int i = 0; i < 20; i++) {
+ _itemPoses[i].x = mapData.readByte();
+ _itemPoses[i].y = mapData.readByte();
+ _itemPoses[i].orient = mapData.readByte();
+ }
}
mapData.skip(32 + 76 + 4 + 20);
diff --git a/engines/gob/map_v2.cpp b/engines/gob/map_v2.cpp
index e2f9207003..15bc709411 100644
--- a/engines/gob/map_v2.cpp
+++ b/engines/gob/map_v2.cpp
@@ -100,11 +100,11 @@ void Map_v2::loadMapObjects(const char *avjFile) {
Common::SeekableReadStream &mapData = *resource->stream();
- _widthByte = mapData.readByte();
- if (_widthByte == 4) {
+ _mapVersion = mapData.readByte();
+ if (_mapVersion == 4) {
_screenWidth = 640;
_screenHeight = 400;
- } else if (_widthByte == 3) {
+ } else if (_mapVersion == 3) {
_passWidth = 65;
_screenWidth = 640;
_screenHeight = 200;
@@ -114,14 +114,14 @@ void Map_v2::loadMapObjects(const char *avjFile) {
_screenHeight = 200;
}
- _wayPointsCount = mapData.readByte();
+ _wayPointCount = mapData.readByte();
_tilesWidth = mapData.readSint16LE();
_tilesHeight = mapData.readSint16LE();
_bigTiles = !(_tilesHeight & 0xFF00);
_tilesHeight &= 0xFF;
- if (_widthByte == 4) {
+ if (_mapVersion == 4) {
_screenWidth = mapData.readSint16LE();
_screenHeight = mapData.readSint16LE();
}
@@ -133,19 +133,19 @@ void Map_v2::loadMapObjects(const char *avjFile) {
mapData.skip(_mapWidth * _mapHeight);
if (resource->getData()[0] == 1)
- wayPointsCount = _wayPointsCount = 40;
+ wayPointsCount = _wayPointCount = 40;
else
- wayPointsCount = _wayPointsCount == 0 ? 1 : _wayPointsCount;
+ wayPointsCount = _wayPointCount == 0 ? 1 : _wayPointCount;
delete[] _wayPoints;
- _wayPoints = new Point[wayPointsCount];
- for (int i = 0; i < _wayPointsCount; i++) {
+ _wayPoints = new WayPoint[wayPointsCount];
+ for (int i = 0; i < _wayPointCount; i++) {
_wayPoints[i].x = mapData.readSByte();
_wayPoints[i].y = mapData.readSByte();
_wayPoints[i].notWalkable = mapData.readSByte();
}
- if (_widthByte == 4) {
+ if (_mapVersion == 4) {
_mapWidth = VAR(17);
_passWidth = _mapWidth;
}
@@ -258,17 +258,24 @@ void Map_v2::findNearestToDest(Mult::Mult_Object *obj) {
void Map_v2::optimizePoints(Mult::Mult_Object *obj, int16 x, int16 y) {
if (obj->nearestWayPoint < obj->nearestDest) {
+
for (int i = obj->nearestWayPoint; i <= obj->nearestDest; i++) {
if (checkDirectPath(obj, x, y, _wayPoints[i].x, _wayPoints[i].y) == 1)
obj->nearestWayPoint = i;
}
+
} else {
- for (int i = obj->nearestWayPoint;
- i >= obj->nearestDest && (_wayPoints[i].notWalkable != 1); i--) {
+
+ for (int i = obj->nearestWayPoint; i >= obj->nearestDest; i++) {
+ if (_wayPoints[i].notWalkable == 1)
+ break;
+
if (checkDirectPath(obj, x, y, _wayPoints[i].x, _wayPoints[i].y) == 1)
obj->nearestWayPoint = i;
}
+
}
+
}
} // End of namespace Gob
diff --git a/engines/gob/module.mk b/engines/gob/module.mk
index 862d1424a8..882a67bd45 100644
--- a/engines/gob/module.mk
+++ b/engines/gob/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/gob
MODULE_OBJS := \
+ console.o \
dataio.o \
detection.o \
draw.o \
@@ -9,7 +10,6 @@ MODULE_OBJS := \
draw_bargon.o \
draw_fascin.o \
draw_playtoons.o \
- driver_vga.o \
expression.o \
game.o \
global.o \
@@ -49,6 +49,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 ab513d78bb..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);
}
}
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..980616bb74 100644
--- a/engines/gob/save/savefile.h
+++ b/engines/gob/save/savefile.h
@@ -33,15 +33,16 @@
namespace Gob {
class GobEngine;
-class SurfaceDesc;
-
-/** A class wrapping a save part header.
- *
- * A save part header consists of 4 fields:
- * ID : The 8 character ID \0SCVMGOB
- * Type : The 4 character ID for this part's type
- * Version : This part's version. Each type has its own version counter
- * Size : The size of the contents, i.e. excluding this header
+class Surface;
+
+/**
+ * A class wrapping a save part header.
+ *
+ * A save part header consists of 4 fields:
+ * ID : The 8 character ID \0SCVMGOB
+ * Type : The 4 character ID for this part's type
+ * Version : This part's version. Each type has its own version counter
+ * Size : The size of the contents, i.e. excluding this header
*/
class SaveHeader {
public:
@@ -162,7 +163,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 +171,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;
@@ -186,13 +187,13 @@ public:
static const uint32 kVersion = 1;
static const uint32 kID = MKID_BE('INFO');
- /** The constructor.
- *
- * @param descMaxLength The maximal number of bytes that fit into the description.
- * @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
- * @param gameVersion An ID for game specific versioning
- * @param endian Endianness of the platform the game originally ran on.
- * @param varCount The number of script variables.
+ /**
+ * The constructor.
+ * @param descMaxLength The maximal number of bytes that fit into the description.
+ * @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
+ * @param gameVersion An ID for game specific versioning
+ * @param endian Endianness of the platform the game originally ran on.
+ * @param varCount The number of script variables.
*/
SavePartInfo(uint32 descMaxLength, uint32 gameID,
uint32 gameVersion, byte endian, uint32 varCount);
@@ -228,10 +229,10 @@ public:
static const uint32 kVersion = 1;
static const uint32 kID = MKID_BE('CONT');
- /** The constructor.
- *
- * @param partCount The number parts this container shall hold.
- * @param slot The save slot this save's for.
+ /**
+ * The constructor.
+ * @param partCount The number parts this container shall hold.
+ * @param slot The save slot this save's for.
*/
SaveContainer(uint32 partCount, uint32 slot);
~SaveContainer();
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 a667d6615d..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;
}
@@ -196,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;
@@ -526,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;
@@ -623,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
@@ -723,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,
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 28123668cc..d643ae511b 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -240,7 +240,7 @@ void AdLib::setKey(byte voice, byte note, bool on, bool spec) {
freq = _freqs[_notLin[voice]][note - octa * 12];
writeOPL(0xA0 + voice, freq & 0xFF);
- writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | 0x20 * on);
+ writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | (0x20 * (on ? 1 : 0)));
if (!freq)
warning("AdLib::setKey Voice %d, note %02X unknown", voice, note);
diff --git a/engines/gob/sound/cdrom.cpp b/engines/gob/sound/cdrom.cpp
index 8f0b1df23e..2e1673b12a 100644
--- a/engines/gob/sound/cdrom.cpp
+++ b/engines/gob/sound/cdrom.cpp
@@ -24,12 +24,12 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "common/util.h"
#include "sound/audiocd.h"
#include "gob/gob.h"
#include "gob/sound/cdrom.h"
-#include "gob/helper.h"
#include "gob/dataio.h"
namespace Gob {
@@ -92,7 +92,7 @@ void CDROM::startTrack(const char *trackName) {
return;
}
- strncpy0(_curTrack, trackName, 15);
+ Common::strlcpy(_curTrack, trackName, 16);
stopPlaying();
_curTrackBuffer = matchPtr;
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 c895c6301d..8e2b3d89cd 100644
--- a/engines/gob/util.cpp
+++ b/engines/gob/util.cpp
@@ -121,6 +121,10 @@ void Util::processInput(bool scroll) {
_fastMode ^= 2;
else if (event.kbd.keycode == Common::KEYCODE_p)
_vm->pauseGame();
+ else if (event.kbd.keycode == Common::KEYCODE_d) {
+ _vm->getDebugger()->attach();
+ _vm->getDebugger()->onFrame();
+ }
break;
}
addKeyToBuffer(event.kbd);
@@ -146,7 +150,7 @@ void Util::processInput(bool scroll) {
// WORKAROUND:
// Force a check of the mouse in order to fix the sofa bug. This apply only for Gob3, and only
// in the impacted TOT file so that the second screen animation is not broken.
- if ((_vm->getGameType() == kGameTypeGob3) && !strncmp(_vm->_game->_curTotFile, "EMAP1008.TOT", 12))
+ if ((_vm->getGameType() == kGameTypeGob3) && !scumm_stricmp(_vm->_game->_curTotFile, "EMAP1008.TOT"))
_vm->_game->evaluateScroll();
}
}
@@ -316,8 +320,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;
}
diff --git a/engines/gob/variables.cpp b/engines/gob/variables.cpp
index 11c5f08bb2..4f6bad52f0 100644
--- a/engines/gob/variables.cpp
+++ b/engines/gob/variables.cpp
@@ -24,10 +24,10 @@
*/
#include "common/endian.h"
+#include "common/str.h"
#include "gob/gob.h"
#include "gob/variables.h"
-#include "gob/helper.h"
namespace Gob {
@@ -112,7 +112,7 @@ uint32 Variables::readOff32(uint32 offset) const {
}
void Variables::readOffString(uint32 offset, char *value, uint32 length) {
- strncpy0(value, (const char *)(_vars + offset), length - 1);
+ Common::strlcpy(value, (const char *)(_vars + offset), length);
}
const uint8 *Variables::getAddressVar8(uint32 var) const {
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..020c72d0c1 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -25,7 +25,6 @@
#include "gob/videoplayer.h"
-#include "gob/helper.h"
#include "gob/global.h"
#include "gob/dataio.h"
#include "gob/video.h"
@@ -155,8 +154,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/font.cpp b/engines/groovie/font.cpp
index dc1d7ae73a..b9d6dbf687 100644
--- a/engines/groovie/font.cpp
+++ b/engines/groovie/font.cpp
@@ -62,6 +62,10 @@ bool T7GFont::load(Common::SeekableReadStream &stream) {
delete[] _glyphs;
_glyphs = new Glyph[numGlyphs];
+ // Ensure we're ready to read the first glyph. (Most versions don't
+ // need it, but the russian one does. This fixes bug #3095031.)
+ stream.seek(glyphOffsets[0]);
+
// Read the glyphs
_maxHeight = _maxWidth = 0;
for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) {
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index cdf5171ab9..131604e75c 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/groovie.h b/engines/groovie/groovie.h
index 8ae5f4157f..f8fad8d91f 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -44,7 +44,7 @@ namespace Common {
* now fully completable. All remaining Groovie games use V2 of the engine,
* which is under slow development.
*
- * Supported games:
+ * Games using this engine:
* - The 7th Guest (completable)
* - The 11th Hour
* - Clandestiny
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index 6959a6a6f1..6ccc68c498 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -427,10 +427,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
index d19eee0041..0064a39434 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -189,21 +189,14 @@ REGISTER_PLUGIN_STATIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine);
namespace Hugo {
void HugoEngine::initGame(const HugoGameDescription *gd) {
- char tmpStr[8];
-
_gameType = gd->gameType;
_platform = gd->desc.platform;
_packedFl = (getFeatures() & GF_PACKED);
_gameVariant = _gameType - 1 + ((_platform == Common::kPlatformWindows) ? 0 : 3);
-//Generate filenames
- if (gd->desc.platform == Common::kPlatformWindows)
- sprintf(tmpStr, "%s%c", gd->desc.gameid, 'w');
- else
- sprintf(tmpStr, "%s%c", gd->desc.gameid, 'd');
-
- sprintf(_initFilename, "%s-00.SAV", tmpStr);
- sprintf(_saveFilename, "%s-%s.SAV", tmpStr, "%d");
+ // 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
index ddd193c2a6..2380ab9f30 100644
--- a/engines/hugo/display.cpp
+++ b/engines/hugo/display.cpp
@@ -34,15 +34,12 @@
#include "common/system.h"
-#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/display.h"
-#include "hugo/file.h"
#include "hugo/util.h"
namespace Hugo {
-#define CENTER -1 // Used to center text in x
#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
@@ -51,21 +48,17 @@ namespace Hugo {
#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)))
-struct rect_t { // Rectangle used in Display list
- int16 x; // Position in dib
- int16 y; // Position in dib
- int16 dx; // width
- int16 dy; // height
-};
+Screen::Screen(HugoEngine *vm) : _vm(vm) {
-Screen::Screen(HugoEngine &vm) : _vm(vm) {
+}
+Screen::~Screen() {
}
void Screen::createPal() {
debugC(1, kDebugDisplay, "createPal");
- g_system->setPalette(_vm._palette, 0, NUM_COLORS);
+ g_system->setPalette(_vm->_palette, 0, NUM_COLORS);
}
void Screen::initDisplay() {
@@ -76,17 +69,16 @@ void Screen::initDisplay() {
// 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
- int16 x;
-
- debugC(3, kDebugDisplay, "moveImage(srcImage, %d, %d, %d, %d, %d, dstImage, %d, %d, %d)", x1, y1, dx, dy, width1, x2, y2, width2);
srcImage += y1 * width1 + x1; // Offset into src image
dstImage += y2 * width2 + x2; // offset into dst image
while (dy--) { // For each row
- for (x = dx; x--;) // For each column
+ for (int16 x = dx; x--;) // For each column
*dstImage++ = *srcImage++;
srcImage += wrap_src; // Wrap to next line
dstImage += wrap_dst;
@@ -136,17 +128,6 @@ void Screen::setBackgroundColor(long color) {
// How??? Translate existing pixels in dib before objects rendered?
}
-// Write the supplied character in the supplied color to x,y pixel coords
-void Screen::writeChar(int16 x, int16 y, char c, byte color) {
- debugC(1, kDebugDisplay, "writeChar(%d, %d, %c, %d)", x, y, c, color);
-
- warning("STUB: writeChar()");
- // x = (int16)((long) x * config.cx / XPIX);
- // y = (int16)((long) y * config.cy / YPIX);
- // SetTextColor(hDC, GetPalIndex(color));
- // TextOut(hDC, x, y, &c, 1);
-}
-
// 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).
@@ -154,7 +135,7 @@ 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
+ 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;
@@ -166,33 +147,27 @@ overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
// 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) {
- overlayState_t overlayState = UNDEF; // Overlay state of object
- image_pt image; // Ptr to object image data
- image_pt subFrontBuffer; // Ptr to offset in _frontBuffer
- image_pt overlay; // Ptr to overlay data
- int16 frontBufferwrap; // Wrap dst_p to next line
- int16 imageWrap; // Wrap src_p to next line
- uint16 x, y; // Index into object data
-
debugC(3, kDebugDisplay, "displayFrame(%d, %d, seq, %d)", sx, sy, (foreFl) ? 1 : 0);
- image = seq->imagePtr; // Source ptr
- subFrontBuffer = &_frontBuffer[sy * XPIX + sx]; // Destination ptr
- overlay = &_vm.getFirstOverlay()[(sy * XPIX + sx) >> 3]; // Overlay ptr
- frontBufferwrap = XPIX - seq->x2 - 1; // Wraps dest_p after each line
- imageWrap = seq->bytesPerLine8 - seq->x2 - 1;
+ 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;
- for (y = 0; y < seq->lines; y++) { // Each line in object
- for (x = 0; x <= seq->x2; x++) {
+ 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
+ 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
+ } else { // No overlay
*subFrontBuffer = *image; // Copy pixel
+ }
}
image++;
subFrontBuffer++;
@@ -225,24 +200,24 @@ void Screen::merge(rect_t *rectA, rect_t *rectB) {
// 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) {
- int16 coalesce[BMAX]; // List of overlapping rects
-
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++)
+ 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
+ if (c == 0) { // None, add a new entry
blist[blen++] = *list;
- else { // At least one overlapping
+ } else { // At least one overlapping
// Merge add-list entry with first blist entry
bp = &blist[coalesce[0]];
merge(list, bp);
@@ -261,15 +236,15 @@ int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int
// 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
- rect_t *p; // Ptr to dlist entry
va_list marker; // Args used for D_ADD operation
-
- debugC(6, kDebugDisplay, "displayList");
+ rect_t *p; // Ptr to dlist entry
switch (update) {
case D_INIT: // Init lists, restore whole screen
@@ -278,7 +253,7 @@ void Screen::displayList(dupdate_t update, ...) {
break;
case D_ADD: // Add a rectangle to list
if (addIndex >= DMAX) {
- Utils::Warn(false, "%s", "Display list exceeded");
+ Utils::Warn("%s", "Display list exceeded");
return;
}
va_start(marker, update); // Initialize variable arguments
@@ -294,8 +269,8 @@ void Screen::displayList(dupdate_t update, ...) {
// 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;
+ if (_vm->getGameStatus().newScreenFl) {
+ _vm->getGameStatus().newScreenFl = false;
break;
}
@@ -304,9 +279,10 @@ void Screen::displayList(dupdate_t update, ...) {
blitLength = mergeLists(addList, blistList, addIndex, blitLength, BMAX);
// Blit the combined blit-list
- for (restoreIndex = 0, p = blistList; restoreIndex < blitLength; restoreIndex++, p++)
+ 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++) {
@@ -320,22 +296,19 @@ void Screen::displayList(dupdate_t update, ...) {
}
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)
- */
-
+// 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 y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
int pixel = y * width + x;
int bitpos = pixel % 8;
@@ -343,8 +316,8 @@ void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) {
byte bitTest = (1 << bitpos);
if ((local_fontdata[2 + offset] & bitTest) == bitTest)
_frontBuffer[(sy + y) * 320 + sx + x] = color;
- //printf("offset: %u, bitpos %u\n", offset, bitpos);
}
+ }
}
// Returns height of characters in current font
@@ -352,40 +325,38 @@ int16 Screen::fontHeight() {
debugC(2, kDebugDisplay, "fontHeight");
static int16 height[NUM_FONTS] = {5, 7, 8};
- return(height[_fnt - FIRST_FONT]);
+ return height[_fnt - FIRST_FONT];
}
// Returns length of supplied string in pixels
-int16 Screen::stringLength(char *s) {
- int16 sum;
- byte **fontArr = _font[_fnt];
-
+int16 Screen::stringLength(const char *s) {
debugC(2, kDebugDisplay, "stringLength(%s)", s);
- for (sum = 0; *s; s++)
+ byte **fontArr = _font[_fnt];
+ int16 sum = 0;
+ for (; *s; s++)
sum += *(fontArr[(uint)*s] + 1) + 1;
- return(sum);
+ return sum;
}
// Return x which would center supplied string
-int16 Screen::center(char *s) {
+int16 Screen::center(const char *s) {
debugC(1, kDebugDisplay, "center(%s)", s);
- return ((int16)((XPIX - stringLength(s)) >> 1));
+ 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, char *s, byte color) {
- byte **font = _font[_fnt];
-
+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;
@@ -393,7 +364,7 @@ void Screen::writeStr(int16 sx, int16 sy, char *s, byte color) {
}
// Shadowed version of writestr
-void Screen::shadowStr(int16 sx, int16 sy, char *s, byte color) {
+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)
@@ -403,44 +374,10 @@ void Screen::shadowStr(int16 sx, int16 sy, char *s, byte color) {
writeStr(sx, sy, s, color);
}
-// Load font file, construct font ptrs and reverse data bytes
-void Screen::loadFont(int16 fontId) {
- byte height, width;
- static bool fontLoadedFl[NUM_FONTS] = {false, false, false};
-
- debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
-
- _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;
- height = *(_fontdata[_fnt] + offset);
- 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;
- }
-}
-
void Screen::userHelp() {
// Introduce user to the game
// DOS versions Only
- Utils::Box(BOX_ANY , "%s",
+ Utils::Box(BOX_ANY , "%s",
"F1 - Press F1 again\n"
" for instructions\n"
"F2 - Sound on/off\n"
@@ -453,4 +390,64 @@ void Screen::userHelp() {
"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()");
+ }
+}
+
+// Initialize screen components and display results
+void Screen::initNewScreenDisplay() {
+ displayList(D_INIT);
+ setBackgroundColor(_TBLACK);
+ displayBackground();
+
+ // Stop premature object display in Display_list(D_DISPLAY)
+ _vm->getGameStatus().newScreenFl = true;
+}
} // End of namespace Hugo
+
diff --git a/engines/hugo/display.h b/engines/hugo/display.h
index 5062664c18..d56f3e55aa 100644
--- a/engines/hugo/display.h
+++ b/engines/hugo/display.h
@@ -32,58 +32,76 @@
#ifndef HUGO_DISPLAY_H
#define HUGO_DISPLAY_H
+
namespace Hugo {
+#define shapeSize 24
-enum overlayState_t {UNDEF, FG, BG}; // Overlay state
-struct rect_t;
+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);
+ Screen(HugoEngine *vm);
+ virtual ~Screen();
+
+ virtual void loadFont(int16 fontId) = 0;
int16 fontHeight();
- int16 stringLength(char *s);
+ 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();
- void loadFont(int16 fontId);
+ void initNewScreenDisplay();
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, char *s, byte color);
+ void shadowStr(int16 sx, int16 sy, const char *s, byte color);
void userHelp();
- void writeChar(int16 x, int16 y, char c, byte color);
- void writeStr(int16 sx, int16 sy, char *s, byte color);
+ 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;
}
-private:
- HugoEngine &_vm;
+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
@@ -94,8 +112,23 @@ private:
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);
- void writeChr(int sx, int sy, byte color, char *local_fontdata);
- int16 center(char *s);
+ int16 center(const char *s);
+};
+
+class Screen_v1d : public Screen {
+public:
+ Screen_v1d(HugoEngine *vm);
+ ~Screen_v1d();
+
+ void loadFont(int16 fontId);
+};
+
+class Screen_v1w : public Screen {
+public:
+ Screen_v1w(HugoEngine *vm);
+ ~Screen_v1w();
+
+ void loadFont(int16 fontId);
};
} // End of namespace Hugo
diff --git a/engines/hugo/display_v1d.cpp b/engines/hugo/display_v1d.cpp
new file mode 100644
index 0000000000..a46f5a8bec
--- /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..3dd8328c43
--- /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
deleted file mode 100644
index ab31f5da93..0000000000
--- a/engines/hugo/engine.cpp
+++ /dev/null
@@ -1,973 +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$
- *
- */
-
-/*
- * 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 = false; // Don't 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.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) {
- static int16 cx, cy; // Save window size, pos
- int16 i;
-
- 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.cx = VIEW_DX * 2; // Window view size
- _config.cy = VIEW_DY * 2;
-
- _config.musicVolume = 85; // Music volume %
- _config.soundVolume = 100; // Sound volume %
- initPlaylist(_config.playlist); // Initialize default tune playlist
-
- file().readBootFile(); // Read startup structure
-
- cx = _config.cx; // Save these around OnFileNew()
- cy = _config.cy;
- break;
- case RESET:
- _config.cx = cx; // Restore cx, cy
- _config.cy = cy;
-
- // Find first tune and play it
- for (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() {
- object_t *obj;
- seq_t *currImage;
- int x1, x2, y1, y2; // object coordinates
- int dx, dy; // Allowable motion wrt boundary
- int8 radius; // Radius for chase (8 bit signed)
-
- 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++) {
- obj = &_objects[i]; // Get pointer to object
- currImage = obj->currImagePtr; // Get ptr to current image
- if (obj->screenIndex == *_screen_p) {
- switch (obj->pathType) {
- case CHASE:
- case CHASE2:
- radius = obj->radius; // Default to object's radius
- if (radius < 0) // If radius infinity, use closer value
- radius = DX;
-
- dx = _hero->x + _hero->currImagePtr->x1 - obj->x - currImage->x1;
- 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++) {
- 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.
- currImage = obj->currImagePtr; // Get ptr to current image
- x1 = obj->x + currImage->x1; // Left edge of object
- x2 = obj->x + currImage->x2; // Right edge
- y1 = obj->y + currImage->y1; // Top edge
- y2 = obj->y + currImage->y2; // Bottom edge
-
- if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
- clearBoundary(x1, x2, y2); // Clear our own boundary
- dx = deltaX(x1, x2, obj->vx, y2);
- if (dx != obj->vx) {
- // An object boundary collision!
- boundaryCollision(obj);
- obj->vx = 0;
- }
-
- 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++) {
- obj = &_objects[i]; // Get pointer to object
- 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
- int b;
-
- 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
- if ((b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 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
- if ((b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 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) {
- int inc, i, j, b;
-
- debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y);
-
- if (vy == 0)
- return(0); // Object stationary
-
- inc = (vy > 0) ? 1 : -1;
- for (j = y + inc; j != (y + vy + inc); j += inc) //Search by byte
- for (i = x1 >> 3; i <= x2 >> 3; i++)
- if ((b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i]) != 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) {
- byte *b; // ptr to boundary byte
-
- debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y);
-
- for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
- 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) {
- int i;
- byte *b; // ptr to boundary byte
-
- debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y);
-
- for (i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
- 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() {
- seq_t *currImage;
- int x1, x2, y1, y2; // hero coordinates
-
- debugC(1, kDebugEngine, "processMaze");
-
- currImage = _hero->currImagePtr; // Get ptr to current image
- x1 = _hero->x + currImage->x1; // Left edge of object
- x2 = _hero->x + currImage->x2; // Right edge
- y1 = _hero->y + currImage->y1; // Top edge
- 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) {
- int ay2, by2;
-
- 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);
-
- ay2 = p1->y + p1->currImagePtr->y2;
- 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() {
- int i, j, num_objs;
- object_t *obj; // Pointer to object
- seq_t *seqPtr; // Save curr_seq_p
- byte objindex[MAX_OBJECTS]; // Array of indeces to objects
-
- debugC(5, kDebugEngine, "updateImages");
-
- // Initialise the index array to visible objects in current screen
- for (i = 0, num_objs = 0; i < _numObj; i++) {
- 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 (i = 0; i < num_objs; i++) {
- 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:
- 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 (i = 0; i < num_objs; i++) {
- 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 (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
- 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 (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) {
- object_t *obj;
- seq_t *curImage;
- int16 objIndex = -1; // Index of found object
- uint16 y2Max = 0; // Greatest y2
- int i;
-
- debugC(3, kDebugEngine, "findObject(%d, %d)", x, y);
-
- // Check objects on screen
- for (i = 0, obj = _objects; i < _numObj; i++, obj++) {
- // Object must be in current screen and "useful"
- if (obj->screenIndex == *_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) {
- curImage = obj->currImagePtr;
- // Object must have a visible image...
- if (curImage != NULL && 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 == NULL && 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) {
-// bool found = false; // TRUE if we found a clear space
- bool foundFl;
- seq_t *curImage = obj->currImagePtr;
- int16 x;
- int16 y = obj->y + curImage->y2 - 1;
-
- debugC(1, kDebugEngine, "findObjectSpace(obj, %d, %d)", *destx, *desty);
-
-// if (!found) // Try left rear corner
- for (foundFl = true, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++)
- if (BOUND(x, y))
- foundFl = false;
-
- if (!foundFl) // Try right rear corner
- for (foundFl = true, *destx = x = 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
- for (foundFl = true, y += 2, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++)
- if (BOUND(x, y))
- foundFl = false;
-
- if (!foundFl) // Try right rear corner
- for (foundFl = true, *destx = x = 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 NULL if none found.
-char *HugoEngine::useBG(char *name) {
- int i;
- objectList_t p = _backgroundObjects[*_screen_p];
-
- debugC(1, kDebugEngine, "useBG(%s)", name);
-
- for (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 (NULL);
-}
-
-// 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) {
- object_t *obj = &_objects[objId]; // Ptr to object
- uses_t *use; // Ptr to use entry
- target_t *target; // Ptr to target entry
- bool foundFl; // TRUE if found target entry
- char *verb; // Background verb to use directly
-
- debugC(1, kDebugEngine, "useObject(%d)", objId);
-
- 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->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])) != NULL)
- 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 (use = _uses; use->objId != _numObj; use++)
- if (_status.inventoryObjId == use->objId) {
- // Look for secondary object, if found use matching verb
- for (foundFl = false, target = use->targets; _arrayNouns[target->nounIndex] != NULL; 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() {
- object_t *obj;
- seq_t *seq;
-
- debugC(1, kDebugEngine, "freeObjects");
-
- // Nothing to do if not allocated yet
- if (_hero->seqList[0].seqPtr == NULL)
- return;
-
- // Free all sequence lists and image data
- for (int i = 0; i < _numObj; i++) {
- obj = &_objects[i];
- for (int j = 0; j < obj->seqNumb; j++) { // for each sequence
- seq = obj->seqList[j].seqPtr; // Free image
- if (seq == NULL) // 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) {
- uint16 *screenAct = _screenActs[screenNum];
-
- debugC(1, kDebugEngine, "screenActions(%d)", 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) {
- int x, y, dx, dy;
- int8 radius; // 8 bits signed
- hotspot_t *hotspot;
-
- debugC(1, kDebugEngine, "boundaryCollision");
-
- if (obj == _hero) {
- // Hotspots only relevant to HERO
- if (obj->vx > 0)
- x = obj->x + obj->currImagePtr->x2;
- else
- x = obj->x + obj->currImagePtr->x1;
- y = obj->y + obj->currImagePtr->y2;
-
- for (int i = 0; _hotspots[i].screenIndex >= 0; i++) {
- 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
- dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1;
- dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2;
- // If object's radius is infinity, use a closer value
- 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() {
- int i;
-
- debugC(1, kDebugEngine, "calcMaxScore");
-
- for (i = 0; i < _numObj; i++)
- _maxscore += _objects[i].objValue;
-
- for (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/file.cpp b/engines/hugo/file.cpp
index 41eb3c88e4..ebda0e9035 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -31,19 +31,18 @@
*/
#include "common/system.h"
-#include "common/file.h"
#include "common/savefile.h"
-#include "hugo/game.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"
+#include "hugo/object.h"
namespace Hugo {
-FileManager::FileManager(HugoEngine &vm) : _vm(vm) {
+FileManager::FileManager(HugoEngine *vm) : _vm(vm) {
}
FileManager::~FileManager() {
@@ -52,18 +51,17 @@ 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
- uint16 r, g, b, i; // Byte index within each plane
- int8 bit; // Bit index within a byte
-
debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
dataPtr += y * bpl * 8; // Point to correct DIB line
- for (r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) // Each byte in all planes
- for (bit = 7; bit >= 0; bit--) // Each bit in byte
+ 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;
}
@@ -71,28 +69,10 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
// 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).
-
- struct { // 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];
- } PCC_header; // Header of a PCC file
-
- byte c, d; // code and data bytes from PCX file
- byte pline[XPIX]; // Hold 4 planes of data
- byte *p = pline; // Ptr to above
- byte i; // PCX repeat count
- uint16 bytesPerLine4; // BPL in 4-bit format
- uint16 size; // Size of image
- uint16 y = 0; // Current line index
-
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();
@@ -112,31 +92,38 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
if (PCC_header.mfctr != 10)
Utils::Error(PCCH_ERR, "%s", name);
- // Allocate memory for seq_t if NULL
- if (seqPtr == NULL)
- if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == NULL)
+ // 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
- bytesPerLine4 = PCC_header.bytesPerLine * 4; // 4-bit bpl
+ 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 = seqPtr->lines * seqPtr->bytesPerLine8;
+ // Size of the image
+ uint16 size = seqPtr->lines * seqPtr->bytesPerLine8;
// Allocate memory for image data if NULL
- if (imagePtr == NULL)
- if ((imagePtr = (byte *)malloc((size_t) size)) == 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) {
- c = f.readByte();
+ byte c = f.readByte();
if ((c & REP_MASK) == REP_MASK) {
- d = f.readByte(); // Read data byte
- for (i = 0; i < (c & LEN_MASK); i++) {
+ 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);
@@ -152,61 +139,59 @@ seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool
void FileManager::readImage(int objNum, object_t *objPtr) {
// Read object file of PCC images into object supplied
- byte x, y, j, k;
- uint16 x2; // Limit on x in image data
- seq_t *seqPtr = 0; // Ptr to sequence structure
- image_pt dibPtr; // Ptr to DIB data
- objBlock_t objBlock; // Info on file within database
- bool firstFl = true; // Initializes pcx read function
-
debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum);
if (!objPtr->seqNumb) // This object has no images
return;
- if (_vm.isPacked()) {
+ 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);
+ 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);
+ 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 (j = 0; j < objPtr->seqNumb; j++) { // for each sequence
- for (k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
+ 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, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ 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, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ 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
- x2 = seqPtr->x2;
+ uint16 x2 = seqPtr->x2;
seqPtr->x1 = seqPtr->x2;
seqPtr->x2 = 0;
seqPtr->y1 = seqPtr->lines;
seqPtr->y2 = 0;
- dibPtr = seqPtr->imagePtr;
- for (y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2)
- for (x = 0; x < x2; x++)
- if (*dibPtr++) { // Some data found
+
+ 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)
@@ -216,7 +201,10 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
if (y > seqPtr->y2)
seqPtr->y2 = y;
}
+ }
+ }
}
+ assert(seqPtr);
seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head
}
@@ -231,36 +219,34 @@ void FileManager::readImage(int objNum, object_t *objPtr) {
case CYCLE_BACKWARD:
objPtr->currImagePtr = seqPtr;
break;
+ default:
+ warning("Unexpected cycling: %d", objPtr->cycling);
}
- if (!_vm.isPacked())
+ 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
-
- static sound_hdr_t s_hdr[MAX_SOUNDS]; // Sound lookup table
- sound_pt soundPtr; // Ptr to sound data
- Common::File fp; // Handle to SOUND_FILE
-// bool music = sound < NUM_TUNES; // TRUE if music, else sound file
-
debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
// No more to do if SILENCE (called for cleanup purposes)
- if (sound == _vm._soundSilence)
- return(NULL);
+ if (sound == _vm->_soundSilence)
+ return 0;
// Open sounds file
+ Common::File fp; // Handle to SOUND_FILE
if (!fp.open(SOUND_FILE)) {
-// Error(FILE_ERR, "%s", SOUND_FILE);
warning("Hugo Error: File not found %s", SOUND_FILE);
- return(NULL);
+ 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);
@@ -272,9 +258,10 @@ sound_pt FileManager::getSound(int16 sound, uint16 *size) {
Utils::Error(SOUND_ERR, "%s", SOUND_FILE);
// Allocate memory for sound or music, if possible
- if ((soundPtr = (byte *)malloc(s_hdr[sound].size)) == 0) {
- Utils::Warn(false, "%s", "Low on memory");
- return(NULL);
+ 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
@@ -297,56 +284,21 @@ bool FileManager::fileExists(char *filename) {
return false;
}
-void FileManager::saveSeq(object_t *obj) {
-// Save sequence number and image number in given object
- byte j, k;
- seq_t *q;
- bool found;
-
- debugC(1, kDebugFile, "saveSeq");
-
- for (j = 0, found = false; !found && (j < obj->seqNumb); j++) {
- q = obj->seqList[j].seqPtr;
- for (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
- int j;
- seq_t *q;
-
- debugC(1, kDebugFile, "restoreSeq");
-
- q = obj->seqList[obj->curSeqNum].seqPtr;
- for (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)
- int i;
- char path[256]; // Full path of saved game
-
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)
- sprintf(path, "%s", _vm._initFilename);
+ path = _vm->_initFilename;
else
- sprintf(path, _vm._saveFilename, slot);
+ path = Common::String::printf(_vm->_saveFilename.c_str(), slot);
- Common::WriteStream *out = 0;
- if (!(out = _vm.getSaveFileManager()->openForSaving(path))) {
- warning("Can't create file '%s', game not saved", path);
+ Common::WriteStream *out = _vm->getSaveFileManager()->openForSaving(path);
+ if (!out) {
+ warning("Can't create file '%s', game not saved", path.c_str());
return;
}
@@ -357,19 +309,19 @@ void FileManager::saveGame(int16 slot, const char *descrip) {
out->write(descrip, DESCRIPLEN);
// Save objects
- for (i = 0; i < _vm._numObj; i++) {
+ 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));
+ _vm->_object->saveSeq(&_vm->_object->_objects[i]);
+ out->write(&_vm->_object->_objects[i], sizeof(object_t));
}
- const status_t &gameStatus = _vm.getGameStatus();
+ const status_t &gameStatus = _vm->getGameStatus();
// Save whether hero image is swapped
- out->write(&_vm._heroImage, sizeof(_vm._heroImage));
+ out->write(&_vm->_heroImage, sizeof(_vm->_heroImage));
// Save score
- int score = _vm.getScore();
+ int score = _vm->getScore();
out->write(&score, sizeof(score));
// Save story mode
@@ -382,16 +334,16 @@ void FileManager::saveGame(int16 slot, const char *descrip) {
out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
// Save screen states
- out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+ out->write(_vm->_screenStates, sizeof(*_vm->_screenStates) * _vm->_numScreens);
// Save points table
- out->write(_vm._points, sizeof(point_t) * _vm._numBonuses);
+ out->write(_vm->_points, sizeof(point_t) * _vm->_numBonuses);
// Now save current time and all current events in event queue
- _vm.scheduler().saveEvents(out);
+ _vm->_scheduler->saveEvents(out);
// Save palette table
- _vm.screen().savePal(out);
+ _vm->_screen->savePal(out);
// Save maze status
out->write(&_maze, sizeof(maze_t));
@@ -403,27 +355,21 @@ void FileManager::saveGame(int16 slot, const char *descrip) {
void FileManager::restoreGame(int16 slot) {
// Restore game from supplied slot number (-1 is INITFILE)
- int i;
- char path[256]; // Full path of saved game
- object_t *p;
- seqList_t seqList[MAX_SEQUENCES];
-// cmdList *cmds; // Save command list pointer
- uint16 cmdIndex; // Save command list pointer
-// char ver[sizeof(VER)]; // Compare versions
-
debugC(1, kDebugFile, "restoreGame(%d)", slot);
// Initialize new-game status
- _vm.initStatus();
+ _vm->initStatus();
// Get full path of saved game file - note test for INITFILE
+ Common::String path; // Full path of saved game
+
if (slot == -1)
- sprintf(path, "%s", _vm._initFilename);
+ path = _vm->_initFilename;
else
- sprintf(path, _vm._saveFilename, slot);
+ path = Common::String::printf(_vm->_saveFilename.c_str(), slot);
- Common::SeekableReadStream *in = 0;
- if (!(in = _vm.getSaveFileManager()->openForLoading(path)))
+ Common::SeekableReadStream *in = _vm->getSaveFileManager()->openForLoading(path);
+ if (!in)
return;
// Check version, can't restore from different versions
@@ -438,50 +384,52 @@ void FileManager::restoreGame(int16 slot) {
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);
+ if (_vm->_heroImage != HERO)
+ _vm->_object->swapImages(HERO, _vm->_heroImage);
// Restore objects, retain current seqList which points to dynamic mem
// Also, retain cmnd_t pointers
- for (i = 0; i < _vm._numObj; i++) {
- p = &_vm._objects[i];
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *p = &_vm->_object->_objects[i];
+ seqList_t seqList[MAX_SEQUENCES];
memcpy(seqList, p->seqList, sizeof(seqList_t));
- cmdIndex = p->cmdIndex;
+ 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));
+ in->read(&_vm->_heroImage, sizeof(_vm->_heroImage));
// If hero swapped in saved game, swap it
- if ((i = _vm._heroImage) != HERO)
- _vm.scheduler().swapImages(HERO, _vm._heroImage);
- _vm._heroImage = i;
+ int heroImg = _vm->_heroImage;
+ if (heroImg != HERO)
+ _vm->_object->swapImages(HERO, _vm->_heroImage);
+ _vm->_heroImage = heroImg;
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
int score;
in->read(&score, sizeof(score));
- _vm.setScore(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);
+ in->read(_vm->_screenStates, sizeof(*_vm->_screenStates) * _vm->_numScreens);
// Restore points table
- in->read(_vm._points, sizeof(point_t) * _vm._numBonuses);
+ in->read(_vm->_points, sizeof(point_t) * _vm->_numBonuses);
// Restore ptrs to currently loaded objects
- for (i = 0; i < _vm._numObj; i++)
- restoreSeq(&_vm._objects[i]);
+ for (int i = 0; i < _vm->_numObj; i++)
+ _vm->_object->restoreSeq(&_vm->_object->_objects[i]);
// Now restore time of the save and the event queue
- _vm.scheduler().restoreEvents(in);
+ _vm->_scheduler->restoreEvents(in);
// Restore palette and change it if necessary
- _vm.screen().restorePal(in);
+ _vm->_screen->restorePal(in);
// Restore maze status
in->read(&_maze, sizeof(maze_t));
@@ -496,109 +444,67 @@ void FileManager::initSavedGame() {
// 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.
- Common::File f; // Handle of saved game file
- char path[256]; // Full path of INITFILE
-
debugC(1, kDebugFile, "initSavedGame");
- // Get full path of INITFILE
- sprintf(path, "%s", _vm._initFilename);
-
-
// Force save of initial game
- if (_vm.getGameStatus().initSaveFl)
+ if (_vm->getGameStatus().initSaveFl)
saveGame(-1, "");
// If initial game doesn't exist, create it
- Common::SeekableReadStream *in = 0;
- if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
+ Common::SeekableReadStream *in = _vm->getSaveFileManager()->openForLoading(_vm->_initFilename);
+ if (!in) {
saveGame(-1, "");
- if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
- Utils::Error(WRITE_ERR, "%s", path);
+ 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();
+ _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", path);
+ if (_vm->getGameStatus().saveSize == -1)
+ Utils::Error(WRITE_ERR, "%s", _vm->_initFilename.c_str());
}
-// Record and playback handling stuff:
-typedef struct {
-// int key; // Character
- uint32 time; // Time at which character was pressed
-} pbdata_t;
-static pbdata_t pbdata;
-FILE *fpb;
-
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)
+ } else if (recordFl) {
fpb = fopen(PBFILE, "wb");
+ }
+*/
pbdata.time = 0; // Say no key available
}
void FileManager::closePlaybackFile() {
- fclose(fpb);
-}
-
-char *FileManager::fetchString(int index) {
-//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat
-// Fetch string from file, decode and return ptr to string in memory
- uint32 off1, off2;
-
- debugC(1, kDebugFile, "fetchString(%d)", index);
-
- // Get offset to string[index] (and next for length calculation)
- _stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET);
- 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;
+// fclose(fpb);
}
void FileManager::printBootText() {
// Read the encrypted text from the boot file and print it
- Common::File ofp;
- int i;
- char *buf;
-
debugC(1, kDebugFile, "printBootText");
+ Common::File ofp;
if (!ofp.open(BOOTFILE)) {
- if (_vm._gameVariant == 3) {
+ if (_vm->_gameVariant == 3) {
//TODO initialize properly _boot structure
warning("printBootText - Skipping as H1 Dos may be a freeware");
return;
- } else
+ } else {
Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ }
}
// Allocate space for the text and print it
- buf = (char *)malloc(_boot.exit_len + 1);
+ 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);
@@ -606,6 +512,7 @@ void FileManager::printBootText() {
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)];
@@ -622,20 +529,17 @@ void FileManager::printBootText() {
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
- byte checksum;
- byte *p;
- Common::File ofp;
- uint32 i;
-
debugC(1, kDebugFile, "readBootFile");
+ Common::File ofp;
if (!ofp.open(BOOTFILE)) {
- if (_vm._gameVariant == 3) {
+ if (_vm->_gameVariant == 3) {
//TODO initialize properly _boot structure
warning("readBootFile - Skipping as H1 Dos may be a freeware");
return;
- } else
+ } else {
Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ }
}
if (ofp.size() < (int32)sizeof(_boot))
@@ -647,8 +551,10 @@ void FileManager::readBootFile() {
ofp.read(_boot.distrib, sizeof(_boot.distrib));
_boot.exit_len = ofp.readUint16LE();
- p = (byte *)&_boot;
- for (i = 0, checksum = 0; i < sizeof(_boot); i++) {
+ byte *p = (byte *)&_boot;
+
+ byte checksum = 0;
+ for (uint32 i = 0; i < sizeof(_boot); i++) {
checksum ^= p[i];
p[i] ^= CRYPT[i % strlen(CRYPT)];
}
@@ -660,16 +566,16 @@ void FileManager::readBootFile() {
uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
// Returns address of uif_hdr[id], reading it in if first call
- static uif_hdr_t UIFHeader[MAX_UIFS]; // Lookup for uif fonts/images
- static bool firstFl = true;
- Common::File ip; // Image data file
-
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);
@@ -688,24 +594,22 @@ uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
void FileManager::readUIFItem(int16 id, byte *buf) {
// Read uif item into supplied buffer.
- Common::File ip; // UIF_FILE handle
- uif_hdr_t *UIFHeaderPtr; // Lookup table of items
- seq_t seq; // Dummy seq_t for image data
-
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
- UIFHeaderPtr = getUIFHeader((uif_t)id);
+ 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, &seq, buf, true, UIF_FILE);
+ readPCX(ip, &dummySeq, buf, true, UIF_FILE);
break;
default: // Read file data into supplied array
if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
@@ -721,457 +625,34 @@ void FileManager::instructions() {
// Only in DOS versions
Common::File f;
- char line[1024], *wrkLine;
- char readBuf[2];
-
- wrkLine = line;
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 */
+ 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.read(readBuf, 2); // Remove CRLF after EOP
}
f.close();
}
+// Read the uif image file (inventory icons)
+void FileManager::readUIFImages() {
+ debugC(1, kDebugFile, "readUIFImages");
-FileManager_v1::FileManager_v1(HugoEngine &vm) : FileManager(vm) {
-}
-
-FileManager_v1::~FileManager_v1() {
-}
-
-void FileManager_v1::openDatabaseFiles() {
- debugC(1, kDebugFile, "openDatabaseFiles");
+ readUIFItem(UIF_IMAGES, _vm->_screen->getGUIBuffer()); // Read all uif images
}
-void FileManager_v1::closeDatabaseFiles() {
- debugC(1, kDebugFile, "closeDatabaseFiles");
-}
-
-void FileManager_v1::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
- uint32 i = 0;
- image_pt tmpImage = image; // temp ptr to overlay 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 (i = 0; i < OVL_SIZE; i++)
- image[i] = 0;
- return;
- }
-
- if (!_sceneryArchive1.open(buf))
- Utils::Error(FILE_ERR, "%s", buf);
-
- _sceneryArchive1.read(tmpImage, OVL_SIZE);
- _sceneryArchive1.close();
-}
-
-void FileManager_v1::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
- seq_t seq; // Image sequence structure for Read_pcx
-
- debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
-
- char *buf = (char *) malloc(2048 + 1); // Buffer for file access
- strcat(strcat(strcpy(buf, _vm._picDir), _vm._screenNames[screenIndex]), BKGEXT);
- if (!_sceneryArchive1.open(buf)) {
- warning("File %s not found, trying again with %s.ART", buf, _vm._screenNames[screenIndex]);
- 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
- readPCX(_sceneryArchive1, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
-
- _sceneryArchive1.close();
-}
-
-FileManager_v2::FileManager_v2(HugoEngine &vm) : FileManager(vm) {
-}
-
-FileManager_v2::~FileManager_v2() {
-}
-
-void FileManager_v2::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_v2::closeDatabaseFiles() {
- debugC(1, kDebugFile, "closeDatabaseFiles");
-
- _stringArchive.close();
- _sceneryArchive1.close();
- _objectsArchive.close();
-}
-
-void FileManager_v2::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
- seq_t seq; // Image sequence structure for Read_pcx
- sceneBlock_t sceneBlock; // Read a database header entry
-
- debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
-
- _sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
-
- 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
- readPCX(_sceneryArchive1, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
-}
-
-void FileManager_v2::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
- uint32 i = 0;
- int16 j, k;
- int8 data; // Must be 8 bits signed
- image_pt tmpImage = image; // temp ptr to overlay file
- sceneBlock_t sceneBlock; // Database header entry
-
- debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
-
- _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
-
- 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();
-
- 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!)
- k = 0; // byte count
- do {
- 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
- j = _sceneryArchive1.readByte();
-
- for (i = 0; i < (byte)(-data + 1); i++, k++)
- *tmpImage++ = j;
- }
- } while (k < OVL_SIZE);
-}
-
-FileManager_v3::FileManager_v3(HugoEngine &vm) : FileManager(vm) {
-}
-
-FileManager_v3::~FileManager_v3() {
-}
-
-void FileManager_v3::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_v3::closeDatabaseFiles() {
- debugC(1, kDebugFile, "closeDatabaseFiles");
-
- _stringArchive.close();
- _sceneryArchive1.close();
- _objectsArchive.close();
-}
-
-void FileManager_v3::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
- seq_t seq; // Image sequence structure for Read_pcx
- sceneBlock_t sceneBlock; // Read a database header entry
-
- debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
-
- _sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
-
- 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
- readPCX(_sceneryArchive1, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
-}
-
-void FileManager_v3::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
- uint32 i = 0;
- image_pt tmpImage = image; // temp ptr to overlay file
- sceneBlock_t sceneBlock; // Database header entry
-
- debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
-
- _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
-
- 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();
-
- 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);
-}
-
-FileManager_v4::FileManager_v4(HugoEngine &vm) : FileManager(vm) {
-}
-
-FileManager_v4::~FileManager_v4() {
-}
-
-void FileManager_v4::readBackground(int screenIndex) {
-// Read a PCX image into dib_a
- seq_t seq; // Image sequence structure for Read_pcx
- sceneBlock_t sceneBlock; // Read a database header entry
- Common::File sceneryArchive;
-
- debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
-
- _sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
-
- 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();
-
- if (screenIndex < 20) {
- _sceneryArchive1.seek(sceneBlock.scene_off, SEEK_SET);
-
- // Read the image into dummy seq and static dib_a
- readPCX(_sceneryArchive1, &seq, _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, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
- }
-}
-
-void FileManager_v4::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_v4::closeDatabaseFiles() {
- debugC(1, kDebugFile, "closeDatabaseFiles");
-
- _stringArchive.close();
- _sceneryArchive1.close();
- _sceneryArchive2.close();
- _objectsArchive.close();
-}
-
-void FileManager_v4::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
-// Open and read in an overlay file, close file
- uint32 i = 0;
- int16 j, k;
- int8 data; // Must be 8 bits signed
- image_pt tmpImage = image; // temp ptr to overlay file
- sceneBlock_t sceneBlock; // Database header entry
- Common::File sceneryArchive;
-
- debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
-
- _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
-
- 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();
-
- 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!)
- k = 0; // byte count
- do {
- 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
- 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!)
- k = 0; // byte count
- do {
- 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
- 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/file.h b/engines/hugo/file.h
index 71bd0bf75c..881cf3c4d7 100644
--- a/engines/hugo/file.h
+++ b/engines/hugo/file.h
@@ -35,18 +35,33 @@
// TODO get rid of those defines
#define HELPFILE "help.dat"
-#define EOP '#' /* Marks end of a page in help file */
+#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);
+ FileManager(HugoEngine *vm);
virtual ~FileManager();
bool fileExists(char *filename);
- char *fetchString(int index);
sound_pt getSound(short sound, uint16 *size);
void closePlaybackFile();
@@ -54,11 +69,10 @@ public:
void instructions();
void readBootFile();
void readImage(int objNum, object_t *objPtr);
+ void readUIFImages();
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;
@@ -66,12 +80,14 @@ public:
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;
+ HugoEngine *_vm;
- Common::File _stringArchive; /* Handle for string file */
- Common::File _sceneryArchive1; /* Handle for scenery file */
- Common::File _objectsArchive; /* Handle for objects file */
+ 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:
@@ -79,6 +95,9 @@ 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();
@@ -86,51 +105,50 @@ private:
// char pbget();
};
-class FileManager_v1 : public FileManager {
+class FileManager_v1d : public FileManager {
public:
- FileManager_v1(HugoEngine &vm);
- ~FileManager_v1();
+ 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_v2 : public FileManager {
+class FileManager_v2d : public FileManager {
public:
- FileManager_v2(HugoEngine &vm);
- ~FileManager_v2();
+ 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_v3 : public FileManager {
+class FileManager_v3d : public FileManager_v2d {
public:
- FileManager_v3(HugoEngine &vm);
- ~FileManager_v3();
+ 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_v4 : public FileManager {
+class FileManager_v1w : public FileManager_v2d {
public:
- FileManager_v4(HugoEngine &vm);
- ~FileManager_v4();
+ FileManager_v1w(HugoEngine *vm);
+ ~FileManager_v1w();
- 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 */
-
};
+
} // 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..115a691ffc
--- /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..5218876caf
--- /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..c4b17b3e65
--- /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..f1e4bf0a15
--- /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
index d032a4b817..869aa1baa4 100644
--- a/engines/hugo/game.h
+++ b/engines/hugo/game.h
@@ -48,13 +48,7 @@ namespace Hugo {
// Copy helpedit\hugow_?.hlp to .\hugowin?.hlp
// Type "PPG" in the game to enter cheat mode.
-#define DEBUG false // Allow me to do special things (EDIT, GOTO)
-#define STORY true // Skip any intro stuff if FALSE
-//#define DATABASE TRUE // Use database instead of individual files. Individual files are used by Hugo1 DOS!
-//#define BETA FALSE // Puts big msg in intro/about box
-#define EPISODE_NUM 1 // Episode we are building
-#define TITLE "Hugo for Windows"
-#define COPYRIGHT "Copyright © 1995-97, David P. Gray"
+#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
@@ -62,7 +56,7 @@ namespace Hugo {
//#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
+//#define VER "v1.5" // 10/07/97 Added order form, new web site
// Game specific equates
#define MAX_TUNES 16 // Max number of tunes
@@ -95,6 +89,7 @@ namespace Hugo {
#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
@@ -111,41 +106,23 @@ namespace Hugo {
// Macros:
#define TPS ((_config.turboFl) ? TURBO_TPS : NORMAL_TPS)
-
-enum TEXTCOLORS {
- _TBLACK, _TBLUE, _TGREEN, _TCYAN,
- _TRED, _TMAGENTA, _TBROWN, _TWHITE,
- _TGRAY, _TLIGHTBLUE, _TLIGHTGREEN, _TLIGHTCYAN,
- _TLIGHTRED, _TLIGHTMAGENTA, _TLIGHTYELLOW, _TBRIGHTWHITE
-};
-
-#define CRYPT "Copyright 1992, David P Gray, Gray Design Associates"
-
-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)
-};
-
#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
-struct uif_hdr_t { // UIF font/image look up
- uint16 size; // Size of uif item
- uint32 offset; // Offset of item in file
+#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};
-// Game specific type definitions
-typedef byte *image_pt; // ptr to an object image (sprite)
-typedef byte *sound_pt; // ptr to sound (or music) data
-
// Enumerate overlay file types
enum ovl_t {BOUNDARY, OVERLAY, OVLBASE};
@@ -213,21 +190,6 @@ 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 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
-};
-
// Following defines the action types and action list
enum action_t { // Parameters:
ANULL = 0xff, // Special NOP used to 'delete' events in DEL_EVENTS
@@ -284,6 +246,37 @@ enum action_t { // Parameters:
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
@@ -803,6 +796,7 @@ struct status_t { // Game status (not saved)
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
@@ -824,8 +818,6 @@ 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
-// int16 wx, wy; // Position of viewport
- int16 cx, cy; // Size of viewport
bool backgroundMusicFl; // Continue music when task inactive
byte musicVolume; // Music volume percentage
byte soundVolume; // Sound volume percentage
@@ -850,7 +842,7 @@ 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 */
+// Structure of scenery file lookup entry
struct sceneBlock_t {
uint32 scene_off;
uint32 scene_len;
@@ -862,7 +854,7 @@ struct sceneBlock_t {
uint32 ob_len;
};
-/* Structure of object file lookup entry */
+// Structure of object file lookup entry
struct objBlock_t {
uint32 objOffset;
uint32 objLength;
diff --git a/engines/hugo/global.h b/engines/hugo/global.h
index 1437eb3734..0ab21ddb9c 100644
--- a/engines/hugo/global.h
+++ b/engines/hugo/global.h
@@ -34,18 +34,19 @@ 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 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 */
+// 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) */
+// 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;
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
index 316c16f018..2a4dd61487 100644
--- a/engines/hugo/hugo.cpp
+++ b/engines/hugo/hugo.cpp
@@ -24,7 +24,9 @@
*/
#include "common/system.h"
+#include "common/random.h"
#include "common/events.h"
+#include "common/EventRecorder.h"
#include "common/debug-channels.h"
#include "hugo/hugo.h"
@@ -37,26 +39,34 @@
#include "hugo/inventory.h"
#include "hugo/parser.h"
#include "hugo/route.h"
+#include "hugo/util.h"
#include "hugo/sound.h"
#include "hugo/intro.h"
+#include "hugo/object.h"
#include "engines/util.h"
namespace Hugo {
-HugoEngine *HugoEngine::s_Engine = NULL;
+HugoEngine *HugoEngine::s_Engine = 0;
overlay_t HugoEngine::_boundary;
overlay_t HugoEngine::_overlay;
overlay_t HugoEngine::_ovlBase;
overlay_t HugoEngine::_objBound;
+config_t _config; // User's config
+maze_t _maze; // 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
+
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)
+ _points(0), _cmdList(0), _screenActs(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), _backgroundObjectsSize(0), _screenActsSize(0), _actListArrSize(0), _usesSize(0)
{
DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
@@ -67,61 +77,85 @@ HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(sy
DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level");
DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level");
DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level");
+ DebugMan.addDebugChannel(kDebugObject, "Object", "Object 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);
+
+ for (int i = 0; _arrayNouns[i]; i++)
+ free(_arrayNouns[i]);
+ free(_arrayNouns);
+
+ for (int i = 0; _arrayVerbs[i]; i++)
+ free(_arrayVerbs[i]);
+ free(_arrayVerbs);
+
free(_screenNames);
+ free(_palette);
free(_textEngine);
free(_textIntro);
+ free(_introX);
+ free(_introY);
free(_textMouse);
free(_textParser);
free(_textSchedule);
free(_textUtil);
-
- warning("Missing: free _arrayNouns");
- warning("Missing: free _arrayVerbs");
-
free(_arrayReqs);
free(_hotspots);
free(_invent);
+
+ for (int i = 0; i < _usesSize; i++)
+ free(_uses[i].targets);
free(_uses);
+
free(_catchallList);
- warning("Missing: free _background_objects");
+ for (int i = 0; i < _backgroundObjectsSize; i++)
+ free(_backgroundObjects[i]);
+ free(_backgroundObjects);
free(_points);
- warning("Missing: free _cmdList");
- warning("Missing: free _screenActs");
- warning("Missing: free _objects");
+ for (int i = 0; i < _cmdListSize; i++)
+ free(_cmdList[i]);
+ free(_cmdList);
+
+ for (int i = 0; i < _screenActsSize; i++)
+ free(_screenActs[i]);
+ free(_screenActs);
+
+ _object->freeObjectArr();
+
+ for (int i = 0; i < _actListArrSize; i++)
+ free(_actListArr[i]);
+ free(_actListArr);
free(_defltTunes);
free(_screenStates);
+
+ if (_arrayFont[0])
+ free(_arrayFont[0]);
+
+ if (_arrayFont[1])
+ free(_arrayFont[1]);
+
+ if (_arrayFont[2])
+ free(_arrayFont[2]);
+
+ delete _object;
+ delete _sound;
+ delete _route;
+ delete _parser;
+ delete _inventory;
+ delete _mouse;
+ delete _screen;
+ delete _scheduler;
+ delete _file;
}
GameType HugoEngine::getGameType() const {
@@ -140,43 +174,59 @@ Common::Error HugoEngine::run() {
s_Engine = this;
initGraphics(320, 200, false);
- _screen = new Screen(*this);
- _mouseHandler = new MouseHandler(*this);
- _inventoryHandler = new InventoryHandler(*this);
- _parser = new Parser(*this);
- _route = new Route(*this);
- _soundHandler = new SoundHandler(*this);
+ _mouse = new MouseHandler(this);
+ _inventory = new InventoryHandler(this);
+ _route = new Route(this);
+ _sound = new SoundHandler(this);
switch (_gameVariant) {
case 0: // H1 Win
- _fileManager = new FileManager_v3(*this);
- _scheduler = new Scheduler_v2(*this);
- _introHandler = new intro_1w(*this);
+ _file = new FileManager_v1w(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v1w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 1:
- _fileManager = new FileManager_v2(*this);
- _scheduler = new Scheduler_v2(*this);
- _introHandler = new intro_2w(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v2w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 2:
- _fileManager = new FileManager_v2(*this);
- _scheduler = new Scheduler_v2(*this);
- _introHandler = new intro_3w(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v1w(this);
+ _intro = new intro_v3w(this);
+ _screen = new Screen_v1w(this);
+ _parser = new Parser_v1w(this);
+ _object = new ObjectHandler_v1w(this);
break;
case 3: // H1 DOS
- _fileManager = new FileManager_v1(*this);
- _scheduler = new Scheduler_v1(*this);
- _introHandler = new intro_1d(*this);
+ _file = new FileManager_v1d(this);
+ _scheduler = new Scheduler_v1d(this);
+ _intro = new intro_v1d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v1d(this);
+ _object = new ObjectHandler_v1d(this);
break;
case 4:
- _fileManager = new FileManager_v2(*this);
- _scheduler = new Scheduler_v1(*this);
- _introHandler = new intro_2d(*this);
+ _file = new FileManager_v2d(this);
+ _scheduler = new Scheduler_v2d(this);
+ _intro = new intro_v2d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v2d(this);
+ _object = new ObjectHandler_v2d(this);
break;
case 5:
- _fileManager = new FileManager_v4(*this);
- _scheduler = new Scheduler_v2(*this);
- _introHandler = new intro_3d(*this);
+ _file = new FileManager_v3d(this);
+ _scheduler = new Scheduler_v3d(this);
+ _intro = new intro_v3d(this);
+ _screen = new Screen_v1d(this);
+ _parser = new Parser_v3d(this);
+ _object = new ObjectHandler_v1d(this);
break;
}
@@ -194,27 +244,25 @@ Common::Error HugoEngine::run() {
initialize();
initConfig(RESET); // Reset user's config
- file().restoreGame(-1);
+ _file->restoreGame(-1);
initMachine();
// Start the state machine
_status.viewState = V_INTROINIT;
- bool doQuitFl = false;
+ _status.doQuitFl = false;
- while (!doQuitFl) {
+ while (!_status.doQuitFl) {
g_system->updateScreen();
- // WORKAROUND: Force the mouse cursor to be displayed. This fixes the disappearing mouse cursor issue.
- g_system->showMouse(true);
runMachine();
// Handle input
Common::Event event;
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
- parser().keyHandler(event.kbd.keycode, 0);
+ _parser->keyHandler(event.kbd.keycode, 0);
break;
case Common::EVENT_MOUSEMOVE:
_mouseX = event.mouse.x;
@@ -233,7 +281,7 @@ Common::Error HugoEngine::run() {
_status.rightButtonFl = false;
break;
case Common::EVENT_QUIT:
- doQuitFl = true;
+ _status.doQuitFl = true;
break;
default:
break;
@@ -244,10 +292,13 @@ Common::Error HugoEngine::run() {
}
void HugoEngine::initMachine() {
- file().readBackground(_numScreens - 1); // Splash screen
+ 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)
+ _file->readUIFImages(); // Read all uif images (only in Win versions)
}
void HugoEngine::runMachine() {
@@ -255,7 +306,6 @@ void HugoEngine::runMachine() {
static uint32 lastTime;
status_t &gameStatus = getGameStatus();
-
// Don't process if we're in a textbox
if (gameStatus.textBoxFl)
return;
@@ -271,43 +321,42 @@ void HugoEngine::runMachine() {
switch (gameStatus.viewState) {
case V_IDLE: // Not processing state machine
- intro().preNewGame(); // Any processing before New Game selected
+ _intro->preNewGame(); // Any processing before New Game selected
break;
case V_INTROINIT: // Initialization before intro begins
- intro().introInit();
+ _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
+ if (_intro->introPlay()) { // Process intro screen
+ _scheduler->newScreen(0); // Initialize first screen
gameStatus.viewState = V_PLAY;
}
break;
case V_PLAY: // Playing game
- 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
- parser().drawStatusText();
- screen().displayList(D_DISPLAY); // Blit the display list to screen
+ g_system->showMouse(true);
+ _parser->charHandler(); // Process user cmd input
+ _object->moveObjects(); // Process object movement
+ _scheduler->runScheduler(); // Process any actions
+ _screen->displayList(D_RESTORE); // Restore previous background
+ _object->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
+ _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;
- int numElem, numSubElem, numSubAct;
- char buf[256];
- int majVer, minVer;
-
in.open("hugo.dat");
if (!in.isOpen()) {
@@ -318,6 +367,7 @@ bool HugoEngine::loadHugoDat() {
}
// Read header
+ char buf[256];
in.read(buf, 4);
buf[4] = '\0';
@@ -328,8 +378,8 @@ bool HugoEngine::loadHugoDat() {
return false;
}
- majVer = in.readByte();
- minVer = in.readByte();
+ 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);
@@ -360,23 +410,32 @@ bool HugoEngine::loadHugoDat() {
// Read palette
_paletteSize = in.readUint16BE();
_palette = (byte *)malloc(sizeof(byte) * _paletteSize);
- for (int i = 0; i < _paletteSize; i++) {
+ for (int i = 0; i < _paletteSize; i++)
_palette[i] = in.readByte();
- }
// Read textEngine
_textEngine = loadTexts(in);
// Read textIntro
- _textIntro = loadTexts(in);
+ _textIntro = loadTextsVariante(in, 0);
// Read x_intro and y_intro
- _introXSize = in.readUint16BE();
- _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();
+ 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
@@ -425,6 +484,7 @@ bool HugoEngine::loadHugoDat() {
}
}
+ int numElem, numSubElem, numSubAct;
//Read _invent
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
@@ -443,6 +503,7 @@ bool HugoEngine::loadHugoDat() {
for (int varnt = 0; varnt < _numVariant; varnt++) {
numElem = in.readUint16BE();
if (varnt == _gameVariant) {
+ _usesSize = numElem;
_uses = (uses_t *)malloc(sizeof(uses_t) * numElem);
for (int i = 0; i < numElem; i++) {
_uses[i].objId = in.readSint16BE();
@@ -496,8 +557,9 @@ bool HugoEngine::loadHugoDat() {
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++) {
+ _backgroundObjectsSize = numElem;
+ _backgroundObjects = (background_t **)malloc(sizeof(background_t *) * _backgroundObjectsSize);
+ for (int i = 0; i < _backgroundObjectsSize; i++) {
numSubElem = in.readUint16BE();
_backgroundObjects[i] = (background_t *)malloc(sizeof(background_t) * numSubElem);
for (int j = 0; j < numSubElem; j++) {
@@ -513,9 +575,9 @@ bool HugoEngine::loadHugoDat() {
for (int i = 0; i < numElem; i++) {
numSubElem = in.readUint16BE();
for (int j = 0; j < numSubElem; j++) {
- in.readUint16BE();;
- in.readUint16BE();;
- in.readSint16BE();;
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readSint16BE();
in.readByte();
in.readByte();
in.readByte();
@@ -544,8 +606,9 @@ bool HugoEngine::loadHugoDat() {
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++) {
+ _cmdListSize = numElem;
+ _cmdList = (cmd **)malloc(sizeof(cmd *) * _cmdListSize);
+ for (int i = 0; i < _cmdListSize; i++) {
numSubElem = in.readUint16BE();
_cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem);
for (int j = 0; j < numSubElem; j++) {
@@ -581,12 +644,13 @@ bool HugoEngine::loadHugoDat() {
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++) {
+ _screenActsSize = numElem;
+ _screenActs = (uint16 **)malloc(sizeof(uint16 *) * _screenActsSize);
+ for (int i = 0; i < _screenActsSize; i++) {
numSubElem = in.readUint16BE();
- if (numSubElem == 0)
+ if (numSubElem == 0) {
_screenActs[i] = 0;
- else {
+ } else {
_screenActs[i] = (uint16 *)malloc(sizeof(uint16) * numSubElem);
for (int j = 0; j < numSubElem; j++)
_screenActs[i][j] = in.readUint16BE();
@@ -599,119 +663,21 @@ bool HugoEngine::loadHugoDat() {
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();
- }
- }
- }
+ _object->loadObjectArr(in);
//#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
+ _hero = &_object->_objects[HERO]; // This always points to hero
+ _screen_p = &(_object->_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++) {
+ _actListArrSize = numElem;
+ _actListArr = (act **)malloc(sizeof(act *) * _actListArrSize);
+ for (int i = 0; i < _actListArrSize; i++) {
numSubElem = in.readUint16BE();
_actListArr[i] = (act *) malloc(sizeof(act) * (numSubElem + 1));
for (int j = 0; j < numSubElem; j++) {
@@ -1332,6 +1298,34 @@ bool HugoEngine::loadHugoDat() {
_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;
}
@@ -1352,6 +1346,7 @@ char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
res = (char **)malloc(sizeof(char *) * numTexts);
res[0] = pos;
in.read(res[0], entryLen);
+ res[0] += DATAALIGNMENT;
} else {
in.read(pos, entryLen);
}
@@ -1374,25 +1369,23 @@ char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
uint16 **HugoEngine::loadLongArray(Common::File &in) {
uint16 **resArray = 0;
- uint16 *resRow = 0;
- uint16 dummy, numRows, numElems;
for (int varnt = 0; varnt < _numVariant; varnt++) {
- numRows = in.readUint16BE();
+ uint16 numRows = in.readUint16BE();
if (varnt == _gameVariant) {
resArray = (uint16 **)malloc(sizeof(uint16 *) * (numRows + 1));
resArray[numRows] = 0;
}
for (int i = 0; i < numRows; i++) {
- numElems = in.readUint16BE();
+ uint16 numElems = in.readUint16BE();
if (varnt == _gameVariant) {
- resRow = (uint16 *)malloc(sizeof(uint16) * numElems);
+ 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++)
- dummy = in.readUint16BE();
+ in.readUint16BE();
}
}
}
@@ -1400,28 +1393,25 @@ uint16 **HugoEngine::loadLongArray(Common::File &in) {
}
char ***HugoEngine::loadTextsArray(Common::File &in) {
- int numNouns;
- int numTexts;
- int entryLen;
- int len;
char ***resArray = 0;
- char **res = 0;
- char *pos = 0;
+ uint16 arraySize;
for (int varnt = 0; varnt < _numVariant; varnt++) {
- numNouns = in.readUint16BE();
+ arraySize = in.readUint16BE();
if (varnt == _gameVariant) {
- resArray = (char ** *)malloc(sizeof(char **) * (numNouns + 1));
- resArray[numNouns] = 0;
+ resArray = (char ***)malloc(sizeof(char **) * (arraySize + 1));
+ resArray[arraySize] = 0;
}
- for (int i = 0; i < numNouns; i++) {
- numTexts = in.readUint16BE();
- entryLen = in.readUint16BE();
- pos = (char *)malloc(entryLen);
+ for (int i = 0; i < arraySize; 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);
}
@@ -1433,7 +1423,7 @@ char ***HugoEngine::loadTextsArray(Common::File &in) {
res[j] = pos;
pos -= 2;
- len = READ_BE_UINT16(pos);
+ int len = READ_BE_UINT16(pos);
pos += 2 + len;
}
@@ -1448,12 +1438,8 @@ char ***HugoEngine::loadTextsArray(Common::File &in) {
char **HugoEngine::loadTexts(Common::File &in) {
int numTexts = in.readUint16BE();
char **res = (char **)malloc(sizeof(char *) * numTexts);
- int entryLen;
- char *pos = 0;
- int len;
-
- entryLen = in.readUint16BE();
- pos = (char *)malloc(entryLen);
+ int entryLen = in.readUint16BE();
+ char *pos = (char *)malloc(entryLen);
in.read(pos, entryLen);
@@ -1462,7 +1448,7 @@ char **HugoEngine::loadTexts(Common::File &in) {
for (int i = 1; i < numTexts; i++) {
pos -= 2;
- len = READ_BE_UINT16(pos);
+ int len = READ_BE_UINT16(pos);
pos += 2 + len;
res[i] = pos;
}
@@ -1478,4 +1464,392 @@ void HugoEngine::freeTexts(char **ptr) {
free(ptr);
}
+// 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");
+
+ _maze.enabledFl = false;
+ _line[0] = '\0';
+
+ _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();
+ _object->freeObjects();
+}
+
+void HugoEngine::readObjectImages() {
+ debugC(1, kDebugEngine, "readObjectImages");
+
+ for (int i = 0; i < _numObj; i++)
+ _file->readImage(i, &_object->_objects[i]);
+}
+
+// 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
+}
+
+// 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);
+ }
+}
+
+// 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;
+}
+
+// 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 (_object->isCarried(i)) // being carried
+ _object->_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);
+ }
+}
+
+// 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 += _object->_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/hugo.h b/engines/hugo/hugo.h
index 532f18d2eb..0964029cab 100644
--- a/engines/hugo/hugo.h
+++ b/engines/hugo/hugo.h
@@ -32,15 +32,29 @@
// 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 19 // 1 byte
-#define DATAALIGNMENT 4
+#define HUGO_DAT_VER_MAJ 0 // 1 byte
+#define HUGO_DAT_VER_MIN 25 // 1 byte
+#define DATAALIGNMENT 4
+#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
namespace Common {
class RandomSource;
}
+/**
+ * This is the namespace of the Hugo engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Hugo's House of Horror
+ * - Whodunit?
+ * - Jungle of Doom
+ */
namespace Hugo {
+
enum GameType {
kGameTypeNone = 0,
kGameTypeHugo1,
@@ -48,21 +62,35 @@ enum GameType {
kGameTypeHugo3
};
-enum HugoebugChannels {
- 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 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,
+ kDebugObject = 1 << 8
};
enum HugoGameFeatures {
GF_PACKED = (1 << 0) // Database
};
+// Strings used by the engine
+enum seqTextEngine {
+ kEsAdvertise = 0
+};
struct HugoGameDescription;
class FileManager;
@@ -74,6 +102,8 @@ class Parser;
class Route;
class SoundHandler;
class IntroHandler;
+class ObjectHandler;
+
class HugoEngine : public Engine {
public:
@@ -97,6 +127,8 @@ public:
byte *_introX;
byte *_introY;
byte *_screenStates;
+ byte *_arrayFont[3];
+ int16 _arrayFontSize[3];
char **_textData;
char **_stringtData;
char **_screenNames;
@@ -112,13 +144,17 @@ public:
hotspot_t *_hotspots;
int16 *_invent;
uses_t *_uses;
+ uint16 _usesSize;
background_t *_catchallList;
background_t **_backgroundObjects;
+ uint16 _backgroundObjectsSize;
point_t *_points;
cmd **_cmdList;
+ uint16 _cmdListSize;
uint16 **_screenActs;
- object_t *_objects;
+ uint16 _screenActsSize;
act **_actListArr;
+ uint16 _actListArrSize;
int16 *_defltTunes;
uint16 _look;
uint16 _take;
@@ -131,8 +167,10 @@ public:
const char *_episode;
const char *_picDir;
- char _initFilename[20];
- char _saveFilename[20];
+ Common::String _initFilename, _saveFilename;
+
+ command_t _statusLine;
+ command_t _scoreLine;
const HugoGameDescription *_gameDescription;
uint32 getFeatures() const;
@@ -143,38 +181,10 @@ public:
// Temporary, until the engine is fully objectified.
static HugoEngine &get() {
- assert(s_Engine != NULL);
+ 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();
@@ -186,23 +196,22 @@ public:
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 boundaryCollision(object_t *obj);
void clearBoundary(int x1, int x2, int y);
void endGame();
+ void initStatus();
+ void processMaze();
+ void readObjectImages();
void readScreenFiles(int screen);
- void setNewScreen(int screen);
- void initNewScreenDisplay();
void screenActions(int screen);
+ void setNewScreen(int screen);
void shutdown();
+ void storeBoundary(int x1, int x2, int y);
+
+ char *useBG(char *name);
+
+ int deltaX(int x1, int x2, int vx, int y);
+ int deltaY(int x1, int x2, int vy, int y);
overlay_t &getBoundaryOverlay() {
return _boundary;
@@ -216,11 +225,9 @@ public:
overlay_t &getFirstOverlay() {
return _overlay;
}
-
status_t &getGameStatus() {
return _status;
}
-
int getScore() const {
return _score;
}
@@ -240,6 +247,17 @@ public:
return _introXSize;
}
+ FileManager *_file;
+ Scheduler *_scheduler;
+ Screen *_screen;
+ MouseHandler *_mouse;
+ InventoryHandler *_inventory;
+ Parser *_parser;
+ Route *_route;
+ SoundHandler *_sound;
+ IntroHandler *_intro;
+ ObjectHandler *_object;
+
protected:
// Engine APIs
@@ -270,16 +288,6 @@ private:
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
@@ -292,19 +300,10 @@ private:
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
diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp
index c2198d87bc..d63a979fe1 100644
--- a/engines/hugo/intro.cpp
+++ b/engines/hugo/intro.cpp
@@ -32,155 +32,15 @@
#include "common/system.h"
-#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/intro.h"
-#include "hugo/file.h"
-#include "hugo/display.h"
-#include "hugo/util.h"
-
namespace Hugo {
-IntroHandler::IntroHandler(HugoEngine &vm) : _vm(vm) {
+IntroHandler::IntroHandler(HugoEngine *vm) : _vm(vm) {
}
IntroHandler::~IntroHandler() {
}
-intro_1w::intro_1w(HugoEngine &vm) : IntroHandler(vm) {
-}
-
-intro_1w::~intro_1w() {
-}
-
-void intro_1w::preNewGame() {
- // Auto-start a new game
- _vm.file().restoreGame(-1);
- _vm.getGameStatus().viewState = V_INTROINIT;
-}
-
-void intro_1w::introInit() {
-}
-
-bool intro_1w::introPlay() {
- return true;
-}
-
-intro_2w::intro_2w(HugoEngine &vm) : IntroHandler(vm) {
-}
-
-intro_2w::~intro_2w() {
-}
-
-void intro_2w::preNewGame() {
-}
-
-void intro_2w::introInit() {
-}
-
-bool intro_2w::introPlay() {
- return true;
-}
-
-intro_3w::intro_3w(HugoEngine &vm) : IntroHandler(vm) {
-}
-
-intro_3w::~intro_3w() {
-}
-
-void intro_3w::preNewGame() {
-}
-
-void intro_3w::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;
-//#endif
-}
-
-bool intro_3w::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
-// SetBkMode(TRANSPARENT);
- if (introTicks < introSize) {
- // Scale viewport x_intro,y_intro to screen (offsetting y)
- _vm.screen().writeChar(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, 'x', _TBRIGHTWHITE);
-
- // 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
-}
-
-intro_1d::intro_1d(HugoEngine &vm) : IntroHandler(_vm) {
-}
-
-intro_1d::~intro_1d() {
-}
-
-void intro_1d::preNewGame() {
-}
-
-void intro_1d::introInit() {
-}
-
-bool intro_1d::introPlay() {
- warning("STUB: intro_1d::introPlay()");
- return true;
-}
-//TODO : Add code for intro H2 DOS
-intro_2d::intro_2d(HugoEngine &vm) : IntroHandler(_vm) {
-}
-
-intro_2d::~intro_2d() {
-}
-
-void intro_2d::preNewGame() {
-}
-
-void intro_2d::introInit() {
-}
-
-bool intro_2d::introPlay() {
- return true;
-}
-
-//TODO : Add code for intro H3 DOS
-intro_3d::intro_3d(HugoEngine &vm) : IntroHandler(_vm) {
-}
-
-intro_3d::~intro_3d() {
-}
-
-void intro_3d::preNewGame() {
-}
-
-void intro_3d::introInit() {
-}
-
-bool intro_3d::introPlay() {
- return true;
-}
-
} // End of namespace Hugo
diff --git a/engines/hugo/intro.h b/engines/hugo/intro.h
index 555ae4326a..f1de01e609 100644
--- a/engines/hugo/intro.h
+++ b/engines/hugo/intro.h
@@ -43,7 +43,7 @@ enum seqTextIntro {
class IntroHandler {
public:
- IntroHandler(HugoEngine &vm);
+ IntroHandler(HugoEngine *vm);
virtual ~IntroHandler();
virtual void preNewGame() = 0;
@@ -51,64 +51,64 @@ public:
virtual bool introPlay() = 0;
protected:
- HugoEngine &_vm;
+ HugoEngine *_vm;
int16 introTicks; // Count calls to introPlay()
};
-class intro_1w : public IntroHandler {
+class intro_v1w : public IntroHandler {
public:
- intro_1w(HugoEngine &vm);
- ~intro_1w();
+ intro_v1w(HugoEngine *vm);
+ ~intro_v1w();
void preNewGame();
void introInit();
bool introPlay();
};
-class intro_1d : public IntroHandler {
+class intro_v1d : public IntroHandler {
public:
- intro_1d(HugoEngine &vm);
- ~intro_1d();
+ intro_v1d(HugoEngine *vm);
+ ~intro_v1d();
void preNewGame();
void introInit();
bool introPlay();
};
-class intro_2w : public IntroHandler {
+class intro_v2w : public IntroHandler {
public:
- intro_2w(HugoEngine &vm);
- ~intro_2w();
+ intro_v2w(HugoEngine *vm);
+ ~intro_v2w();
void preNewGame();
void introInit();
bool introPlay();
};
-class intro_2d : public IntroHandler {
+class intro_v2d : public IntroHandler {
public:
- intro_2d(HugoEngine &vm);
- ~intro_2d();
+ intro_v2d(HugoEngine *vm);
+ ~intro_v2d();
void preNewGame();
void introInit();
bool introPlay();
};
-class intro_3w : public IntroHandler {
+class intro_v3w : public IntroHandler {
public:
- intro_3w(HugoEngine &vm);
- ~intro_3w();
+ intro_v3w(HugoEngine *vm);
+ ~intro_v3w();
void preNewGame();
void introInit();
bool introPlay();
};
-class intro_3d : public IntroHandler {
+class intro_v3d : public IntroHandler {
public:
- intro_3d(HugoEngine &vm);
- ~intro_3d();
+ intro_v3d(HugoEngine *vm);
+ ~intro_v3d();
void preNewGame();
void introInit();
diff --git a/engines/hugo/intro_v1d.cpp b/engines/hugo/intro_v1d.cpp
new file mode 100644
index 0000000000..61626e8172
--- /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..74efc4ff8c
--- /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..0c9f85d1ea
--- /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..414846bfc7
--- /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..8e0c0cc22c
--- /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..a0bc624760
--- /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
index c5f09ad653..ad0d5cbce9 100644
--- a/engines/hugo/inventory.cpp
+++ b/engines/hugo/inventory.cpp
@@ -40,12 +40,13 @@
#include "hugo/mouse.h"
#include "hugo/inventory.h"
#include "hugo/parser.h"
+#include "hugo/object.h"
namespace Hugo {
#define MAX_DISP (XPIX / INV_DX) // Max icons displayable
-InventoryHandler::InventoryHandler(HugoEngine &vm) : _vm(vm) {
+InventoryHandler::InventoryHandler(HugoEngine *vm) : _vm(vm) {
}
// Construct the inventory scrollbar in dib_i
@@ -54,17 +55,15 @@ InventoryHandler::InventoryHandler(HugoEngine &vm) : _vm(vm) {
// 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) {
- int16 ux, uy, ix; // Coordinates of icons
-
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()));
+ 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);
+ _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;
@@ -73,19 +72,19 @@ void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, b
int16 displayed = 0;
int16 carried = 0;
for (int16 i = 0; i < imageTotNumb; i++) {
- if (_vm._objects[_vm._invent[i]].carriedFl) {
+ if (_vm->_object->isCarried(_vm->_invent[i])) {
// Check still room to display and past first scroll index
if (displayed < displayNumb && carried >= firstObjId) {
// Compute source coordinates in dib_u
- ux = (i + NUM_ARROWS) * INV_DX % XPIX;
- uy = (i + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
+ int16 ux = (i + NUM_ARROWS) * INV_DX % XPIX;
+ int16 uy = (i + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
// Compute dest coordinates in dib_i
- ix = ((scrollFl) ? displayed + 1 : displayed) * INV_DX;
+ 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);
+ _vm->_screen->moveImage(_vm->_screen->getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm->_screen->getIconBuffer(), ix, 0, XPIX);
}
carried++; // Count number carried
}
@@ -95,24 +94,23 @@ void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, b
// 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 i, j;
- int16 objId = -1; // Return objid under cursor
+
int16 imageNumb; // Total number of inventory items
int displayNumb; // Total number displayed/carried
- int16 cursorx, cursory; // Current cursor position
- bool scrollFl; // TRUE if scroll arrows needed
- va_list marker; // Args used for D_ADD operation
-
- debugC(1, kDebugInventory, "processInventory(invact_t action, ...)");
-
// 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)
+ for (imageNumb = 0, displayNumb = 0; imageNumb < _vm->_maxInvent && _vm->_invent[imageNumb] != -1; imageNumb++) {
+ if (_vm->_object->isCarried(_vm->_invent[imageNumb]))
displayNumb++;
+ }
// Will we need the scroll arrows?
- scrollFl = displayNumb > MAX_DISP;
+ 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
@@ -135,11 +133,11 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
cursory -= DIBOFF_Y; // Icon bar is at true zero
if (cursory > 0 && cursory < INV_DY) { // Within icon bar?
- i = cursorx / INV_DX; // Compute icon index
- if (scrollFl) { // Scroll buttons displayed
- if (i == 0) // Left scroll button
+ int16 i = cursorx / INV_DX; // Compute icon index
+ if (scrollFl) { // Scroll buttons displayed
+ if (i == 0) { // Left scroll button
objId = LEFT_ARROW;
- else {
+ } else {
if (i == MAX_DISP - 1) // Right scroll button
objId = RIGHT_ARROW;
else // Adjust for scroll
@@ -148,12 +146,16 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
}
// If not an arrow, find object id - limit to valid range
- if (objId == -1 && i < displayNumb)
+ if (objId == -1 && i < displayNumb) {
// Find objid by counting # carried objects == i+1
- for (j = 0, i++; i > 0 && j < _vm._numObj; j++)
- if (_vm._objects[j].carriedFl)
+ int16 j;
+ for (j = 0, i++; i > 0 && j < _vm->_numObj; j++) {
+ if (_vm->_object->isCarried(j)) {
if (--i == 0)
objId = j;
+ }
+ }
+ }
}
break;
}
@@ -161,7 +163,7 @@ int16 InventoryHandler::processInventory(invact_t action, ...) {
}
void InventoryHandler::runInventory() {
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
debugC(1, kDebugInventory, "runInventory");
@@ -175,15 +177,15 @@ void InventoryHandler::runInventory() {
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);
+ _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
+ _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->_object->updateImages(); // Add objects back into display list for restore
gameStatus.inventoryState = I_OFF;
gameStatus.viewState = V_PLAY;
}
@@ -193,9 +195,9 @@ void InventoryHandler::runInventory() {
// 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
+ _vm->_screen->displayList(D_RESTORE); // Restore _frontBuffer
+ _vm->_object->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
@@ -203,8 +205,8 @@ void InventoryHandler::runInventory() {
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);
+ _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
@@ -212,17 +214,17 @@ void InventoryHandler::runInventory() {
// 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);
+ _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
+ _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;
}
}
diff --git a/engines/hugo/inventory.h b/engines/hugo/inventory.h
index 5cc1af28c2..36fca71a43 100644
--- a/engines/hugo/inventory.h
+++ b/engines/hugo/inventory.h
@@ -40,13 +40,13 @@ namespace Hugo {
class InventoryHandler {
public:
- InventoryHandler(HugoEngine &vm);
+ InventoryHandler(HugoEngine *vm);
int16 processInventory(invact_t action, ...);
void runInventory();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
void constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId);
};
diff --git a/engines/hugo/module.mk b/engines/hugo/module.mk
index f7aa45a2c2..75c9f9d5e2 100644
--- a/engines/hugo/module.mk
+++ b/engines/hugo/module.mk
@@ -3,15 +3,39 @@ MODULE := engines/hugo
MODULE_OBJS := \
detection.o \
display.o \
- engine.o \
+ display_v1d.o \
+ display_v1w.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 \
+ object.o \
+ object_v1d.o \
+ object_v1w.o \
+ object_v2d.o \
+ object_v3d.o \
parser.o \
+ parser_v1w.o \
+ parser_v1d.o \
+ parser_v2d.o \
+ parser_v3d.o \
route.o \
schedule.o \
+ schedule_v1d.o \
+ schedule_v1w.o \
+ schedule_v2d.o \
+ schedule_v3d.o \
sound.o \
util.o
diff --git a/engines/hugo/mouse.cpp b/engines/hugo/mouse.cpp
index a3f695b92a..9fc8a33373 100644
--- a/engines/hugo/mouse.cpp
+++ b/engines/hugo/mouse.cpp
@@ -43,6 +43,7 @@
#include "hugo/inventory.h"
#include "hugo/route.h"
#include "hugo/util.h"
+#include "hugo/object.h"
namespace Hugo {
@@ -59,83 +60,78 @@ enum seqTextMouse {
kMsExit = 1
};
-MouseHandler::MouseHandler(HugoEngine &vm) : _vm(vm) {
+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);
- if (_vm.getPlatform() == Common::kPlatformWindows)
- _vm.screen().loadFont(fontId);
+ _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 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);
+ _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) {
- int i;
- hotspot_t *hotspot;
-
debugC(2, kDebugMouse, "findExit(%d, %d)", cx, cy);
- for (i = 0, hotspot = _vm._hotspots; hotspot->screenIndex >= 0; i++, hotspot++)
- if (hotspot->screenIndex == *_vm._screen_p)
+ 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);
+ 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) {
- object_t *obj;
- int16 x, y;
- bool foundFl = false; // TRUE if route found to object
-
debugC(1, kDebugMouse, "Process_rclick(%d, %d, %d)", objId, cx, cy);
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ 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
+ gameStatus.inventoryObjId = -1; // Same icon - deselect it
else
- _vm.useObject(objId); // Use status.objid on object
+ _vm->_object->useObject(objId); // Use status.objid on object
} else { // Clicked over viewport object
- obj = &_vm._objects[objId];
+ object_t *obj = &_vm->_object->_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);
+ if (_vm->_object->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->_object->useObject(objId);
break;
case 0: // Immediate use
- _vm.useObject(objId); // Pick up or use object
+ _vm->_object->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
+ if (!_vm->_route->startRoute(GO_GET, objId, obj->viewx, obj->viewy)) {
+ if (_vm->_hero->cycling == INVISIBLE)// If invisible do
+ _vm->_object->useObject(objId); // immediate use
else
- Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ Utils::Box(BOX_ANY, "%s", _vm->_textMouse[kMsNoWayText]); // Can't get there
}
break;
}
@@ -149,47 +145,46 @@ void MouseHandler::processRightClick(int16 objId, int16 cx, int16 cy) {
// 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;
- bool foundFl = false; // TRUE if route found to object
-
- debugC(1, kDebugMouse, "Process_lclick(%d, %d, %d)", objId, cx, cy);
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ 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);
+ _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);
+ _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;
+ 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);
+ _vm->_scheduler->insertActionList(_vm->_hotspots[i].actIndex);
} else { // Set up route to exit spot
- if (_vm._hotspots[i].direction == Common::KEYCODE_RIGHT)
+ if (_vm->_hotspots[i].direction == Common::KEYCODE_RIGHT)
x -= HERO_MAX_WIDTH;
- else if (_vm._hotspots[i].direction == Common::KEYCODE_LEFT)
+ 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
+ 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
@@ -197,28 +192,29 @@ void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
}
break;
default: // Look at an icon or object
- obj = &_vm._objects[objId];
+ obj = &_vm->_object->_objects[objId];
// Over iconbar - immediate description
if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y)
- _vm.lookObject(obj);
+ _vm->_object->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);
+ if (_vm->_object->findObjectSpace(obj, &x, &y))
+ foundFl = _vm->_route->startRoute(GO_LOOK, objId, x, y);
+ if (!foundFl) // Can't get there, immediate description
+ _vm->_object->lookObject(obj);
break;
case 0: // Immediate description
- _vm.lookObject(obj);
+ _vm->_object->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
+ if (!_vm->_route->startRoute(GO_LOOK, objId, obj->viewx, obj->viewy)) {
+ if (_vm->_hero->cycling == INVISIBLE) // If invisible do
+ _vm->_object->lookObject(obj); // immediate decription
else
- Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ Utils::Box(BOX_ANY, "%s", _vm->_textMouse[kMsNoWayText]); // Can't get there
}
break;
}
@@ -229,20 +225,14 @@ void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
// Process mouse activity
void MouseHandler::mouseHandler() {
- int16 iconId; // Find index of dragged icon
- int iconx, icony; // Icon position (in dib_a)
- int16 ux, uy; // Icon position (in dib_u)
- int16 objId = -1; // Current source object
- char *name; // Name of object to display
-
debugC(2, kDebugMouse, "mouseHandler");
- int16 cx = _vm.getMouseX();
- int16 cy = _vm.getMouseY();
+ int16 cx = _vm->getMouseX();
+ int16 cy = _vm->getMouseY();
- status_t &gameStatus = _vm.getGameStatus();
+ status_t &gameStatus = _vm->getGameStatus();
- gameStatus.cx = cx; // Save cursor coords
+ gameStatus.cx = cx; // Save cursor coords
gameStatus.cy = cy;
// Don't process if outside client area
@@ -252,36 +242,39 @@ void MouseHandler::mouseHandler() {
// Display dragged inventory icon if one currently selected
if (gameStatus.inventoryObjId != -1) {
// Find index of icon
- for (iconId = 0; iconId < _vm._maxInvent; iconId++)
- if (gameStatus.inventoryObjId == _vm._invent[iconId])
+ 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
- ux = (iconId + NUM_ARROWS) * INV_DX % XPIX;
- uy = (iconId + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
+ int16 ux = (iconId + NUM_ARROWS) * INV_DX % XPIX;
+ int16 uy = (iconId + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
// Compute destination coordinates in dib_a
- iconx = cx + IX_OFF;
- icony = cy + IY_OFF;
+ 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);
+ _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);
+ objId = _vm->_inventory->processInventory(INV_GET, cx, cy);
if (objId == -1) // No match, check rest of view
- objId = _vm.findObject(cx, cy);
+ objId = _vm->_object->findObject(cx, cy);
if (objId >= 0) { // Got a match
// Display object name next to cursor (unless CURSOR_NOCHAR)
// Note test for swapped hero name
- name = _vm._arrayNouns[_vm._objects[(objId == HERO) ? _vm._heroImage : objId].nounIndex][CURSOR_NAME];
+ char *name = _vm->_arrayNouns[_vm->_object->_objects[(objId == HERO) ? _vm->_heroImage : objId].nounIndex][CURSOR_NAME];
if (name[0] != CURSOR_NOCHAR)
cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE);
@@ -293,9 +286,9 @@ void MouseHandler::mouseHandler() {
// Process cursor over an exit hotspot
if (objId == -1) {
int i = findExit(cx, cy);
- if (i != -1 && _vm._hotspots[i].viewx >= 0) {
+ if (i != -1 && _vm->_hotspots[i].viewx >= 0) {
objId = EXIT_HOTSPOT;
- cursorText(_vm._textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE);
+ cursorText(_vm->_textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE);
}
}
diff --git a/engines/hugo/mouse.h b/engines/hugo/mouse.h
index 3ac5f19f32..0fcb651b3a 100644
--- a/engines/hugo/mouse.h
+++ b/engines/hugo/mouse.h
@@ -36,12 +36,12 @@ namespace Hugo {
class MouseHandler {
public:
- MouseHandler(HugoEngine &vm);
+ MouseHandler(HugoEngine *vm);
void mouseHandler();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
void cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color);
int16 findExit(int16 cx, int16 cy);
diff --git a/engines/hugo/object.cpp b/engines/hugo/object.cpp
new file mode 100644
index 0000000000..77a25ca343
--- /dev/null
+++ b/engines/hugo/object.cpp
@@ -0,0 +1,429 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+
+namespace Hugo {
+
+ObjectHandler::ObjectHandler(HugoEngine *vm) : _vm(vm) {
+}
+
+ObjectHandler::~ObjectHandler() {
+}
+
+void ObjectHandler::saveSeq(object_t *obj) {
+// Save sequence number and image number in given object
+ debugC(1, kDebugObject, "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 ObjectHandler::restoreSeq(object_t *obj) {
+// Set up cur_seq_p from stored sequence and image number in object
+ debugC(1, kDebugObject, "restoreSeq");
+
+ seq_t *q = obj->seqList[obj->curSeqNum].seqPtr;
+ for (int j = 0; j < obj->curImageNum; j++)
+ q = q->nextSeqPtr;
+ obj->currImagePtr = q;
+}
+
+// If status.objid = -1, pick up objid, else use status.objid on objid,
+// if objid can't be picked up, use it directly
+void ObjectHandler::useObject(int16 objId) {
+ debugC(1, kDebugObject, "useObject(%d)", objId);
+
+ char *verb; // Background verb to use directly
+ object_t *obj = &_objects[objId]; // Ptr to object
+ if (_vm->getGameStatus().inventoryObjId == -1) {
+ // Get or use objid directly
+ if ((obj->genericCmd & TAKE) || obj->objValue) // Get collectible item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_take][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & LOOK) // Look item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_look][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & DROP) // Drop item
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_drop][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if (obj->cmdIndex != 0) // Use non-collectible item if able
+ sprintf(_line, "%s %s", _vm->_arrayVerbs[_vm->_cmdList[obj->cmdIndex][1].verbIndex][0], _vm->_arrayNouns[obj->nounIndex][0]);
+ else if ((verb = _vm->useBG(_vm->_arrayNouns[obj->nounIndex][0])) != 0)
+ sprintf(_line, "%s %s", verb, _vm->_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", _vm->_arrayVerbs[_vm->_cmdList[_objects[_vm->getGameStatus().inventoryObjId].cmdIndex][1].verbIndex][0],
+ _vm->_arrayNouns[_objects[_vm->getGameStatus().inventoryObjId].nounIndex][0],
+ _vm->_arrayNouns[obj->nounIndex][0]);
+
+ // Check valid use of objects and override verb if necessary
+ for (uses_t *use = _vm->_uses; use->objId != _vm->_numObj; use++) {
+ if (_vm->getGameStatus().inventoryObjId == use->objId) {
+ // Look for secondary object, if found use matching verb
+ bool foundFl = false;
+ for (target_t *target = use->targets; _vm->_arrayNouns[target->nounIndex] != 0; target++)
+ if (_vm->_arrayNouns[target->nounIndex][0] == _vm->_arrayNouns[obj->nounIndex][0]) {
+ foundFl = true;
+ sprintf(_line, "%s %s %s", _vm->_arrayVerbs[target->verbIndex][0],
+ _vm->_arrayNouns[_objects[_vm->getGameStatus().inventoryObjId].nounIndex][0],
+ _vm->_arrayNouns[obj->nounIndex][0]);
+ }
+
+ // No valid use of objects found, print failure string
+ if (!foundFl) {
+ // Deselect dragged icon if inventory not active
+ if (_vm->getGameStatus().inventoryState != I_ACTIVE)
+ _vm->getGameStatus().inventoryObjId = -1;
+ Utils::Box(BOX_ANY, "%s", _vm->_textData[use->dataIndex]);
+ return;
+ }
+ }
+ }
+ }
+
+ if (_vm->getGameStatus().inventoryState == I_ACTIVE) // If inventory active, remove it
+ _vm->getGameStatus().inventoryState = I_UP;
+ _vm->getGameStatus().inventoryObjId = -1; // Deselect any dragged icon
+ _vm->_parser->lineHandler(); // and process command
+}
+
+// Return object index of the topmost object under the cursor, or -1 if none
+// Objects are filtered if not "useful"
+int16 ObjectHandler::findObject(uint16 x, uint16 y) {
+ debugC(3, kDebugObject, "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 < _vm->_numObj; i++, obj++) {
+ // Object must be in current screen and "useful"
+ if (obj->screenIndex == *_vm->_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;
+}
+
+// Issue "Look at <object>" command
+// Note special case of swapped hero image
+void ObjectHandler::lookObject(object_t *obj) {
+ debugC(1, kDebugObject, "lookObject");
+
+ if (obj == _vm->_hero)
+ // Hero swapped - look at other
+ obj = &_objects[_vm->_heroImage];
+
+ _vm->_parser->command("%s %s", _vm->_arrayVerbs[_vm->_look][0], _vm->_arrayNouns[obj->nounIndex][0]);
+}
+
+// Free all object images
+void ObjectHandler::freeObjects() {
+ debugC(1, kDebugObject, "freeObjects");
+
+ // Nothing to do if not allocated yet
+ if (_vm->_hero->seqList[0].seqPtr == 0)
+ return;
+
+ // Free all sequence lists and image data
+ for (int i = 0; i < _vm->_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
+ }
+ }
+}
+
+// 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 ObjectHandler::y2comp(const void *a, const void *b) {
+ debugC(6, kDebugObject, "y2comp");
+
+// const object_t *p1 = &s_Engine->_objects[*(const byte *)a];
+// const object_t *p2 = &s_Engine->_objects[*(const byte *)b];
+ const object_t *p1 = &HugoEngine::get()._object->_objects[*(const byte *)a];
+ const object_t *p2 = &HugoEngine::get()._object->_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;
+}
+
+// Return TRUE if object being carried by hero
+bool ObjectHandler::isCarrying(uint16 wordIndex) {
+ debugC(1, kDebugObject, "isCarrying(%d)", wordIndex);
+
+ for (int i = 0; i < _vm->_numObj; i++) {
+ if ((wordIndex == _objects[i].nounIndex) && _objects[i].carriedFl)
+ return true;
+ }
+ return false;
+}
+
+// Describe any takeable objects visible in this screen
+void ObjectHandler::showTakeables() {
+ debugC(1, kDebugObject, "showTakeables");
+
+ for (int j = 0; j < _vm->_numObj; j++) {
+ object_t *obj = &_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]);
+ }
+ }
+}
+
+// Find a clear space around supplied object that hero can walk to
+bool ObjectHandler::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) {
+ debugC(1, kDebugObject, "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;
+}
+
+void ObjectHandler::freeObjectArr() {
+ free(_objects);
+}
+
+void ObjectHandler::loadObjectArr(Common::File &in) {
+ debugC(6, kDebugObject, "loadObject(&in)");
+
+// TODO: For Hugo3, if not in story mode, set _objects[2].state to 3
+ for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
+ uint16 numElem = in.readUint16BE();
+ if (varnt == _vm->_gameVariant) {
+ _objCount = numElem;
+ _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();
+ uint16 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();
+ uint16 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();
+ }
+ }
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object.h b/engines/hugo/object.h
new file mode 100644
index 0000000000..72b13caa8f
--- /dev/null
+++ b/engines/hugo/object.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 original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_OBJECT_H
+#define HUGO_OBJECT_H
+
+#include "common/file.h"
+
+#define MAXOBJECTS 128 // Used in Update_images()
+#define BOUND(X, Y) ((_vm->getBoundaryOverlay()[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0) // Boundary bit set
+
+namespace Hugo {
+
+class ObjectHandler {
+public:
+ ObjectHandler(HugoEngine *vm);
+ virtual ~ObjectHandler();
+
+ object_t *_objects;
+
+ virtual void moveObjects() = 0;
+ virtual void updateImages() = 0;
+ virtual void swapImages(int objNumb1, int objNumb2) = 0;
+
+ bool isCarrying(uint16 wordIndex);
+ bool findObjectSpace(object_t *obj, int16 *destx, int16 *desty);
+
+ int16 findObject(uint16 x, uint16 y);
+ void freeObjects();
+ void loadObjectArr(Common::File &in);
+ void freeObjectArr();
+ void lookObject(object_t *obj);
+ void restoreSeq(object_t *obj);
+ void saveSeq(object_t *obj);
+ void showTakeables();
+ void useObject(int16 objId);
+
+ static int y2comp(const void *a, const void *b);
+
+ bool isCarried(int objIndex) {
+ return _objects[objIndex].carriedFl;
+ }
+
+ void setCarry(int objIndex, bool val) {
+ _objects[objIndex].carriedFl = val;
+ }
+
+ void setVelocity(int objIndex, int8 vx, int8 vy) {
+ _objects[objIndex].vx = vx;
+ _objects[objIndex].vy = vy;
+ }
+
+ void setPath(int objIndex, path_t pathType, int16 vxPath, int16 vyPath) {
+ _objects[objIndex].pathType = pathType;
+ _objects[objIndex].vxPath = vxPath;
+ _objects[objIndex].vyPath = vyPath;
+ }
+protected:
+ HugoEngine *_vm;
+ uint16 _objCount;
+};
+
+class ObjectHandler_v1d : public ObjectHandler {
+public:
+ ObjectHandler_v1d(HugoEngine *vm);
+ virtual ~ObjectHandler_v1d();
+
+ void moveObjects();
+ void updateImages();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+class ObjectHandler_v1w : public ObjectHandler {
+public:
+ ObjectHandler_v1w(HugoEngine *vm);
+ ~ObjectHandler_v1w();
+
+ void moveObjects();
+ void updateImages();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+class ObjectHandler_v2d : public ObjectHandler_v1d {
+public:
+ ObjectHandler_v2d(HugoEngine *vm);
+ virtual ~ObjectHandler_v2d();
+
+ void moveObjects();
+ void updateImages();
+};
+
+class ObjectHandler_v3d : public ObjectHandler_v2d {
+public:
+ ObjectHandler_v3d(HugoEngine *vm);
+ ~ObjectHandler_v3d();
+
+ void moveObjects();
+ void swapImages(int objNumb1, int objNumb2);
+};
+
+} // End of namespace Hugo
+#endif //HUGO_OBJECT_H
diff --git a/engines/hugo/object_v1d.cpp b/engines/hugo/object_v1d.cpp
new file mode 100644
index 0000000000..b2801d14c1
--- /dev/null
+++ b/engines/hugo/object_v1d.cpp
@@ -0,0 +1,358 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v1d::ObjectHandler_v1d(HugoEngine *vm) : ObjectHandler(vm) {
+}
+
+ObjectHandler_v1d::~ObjectHandler_v1d() {
+}
+
+// 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 ObjectHandler_v1d::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_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:
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, false);
+ break;
+ case CYCLE_FORWARD:
+ if (obj->frameTimer) // Not time to see next frame yet
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, false);
+ else
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, false);
+ break;
+ case CYCLE_BACKWARD: {
+ seq_t *seqPtr = obj->currImagePtr;
+ if (!obj->frameTimer) { // Show next frame
+ while (seqPtr->nextSeqPtr != obj->currImagePtr)
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+ _vm->_screen->displayFrame(obj->x, obj->y, seqPtr, false);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ _vm->_scheduler->waitForRefresh();
+
+ // 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;
+ }
+ }
+}
+
+// Update all object positions. Process object 'local' events
+// including boundary events and collisions
+void ObjectHandler_v1d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ static int dxOld, dyOld; // previous directions for CHASEing
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_screen_p) {
+ switch (obj->pathType) {
+ case CHASE: {
+ // Allowable motion wrt boundary
+ int dx = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= 1)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= 1)
+ 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)
+ if (obj->seqNumb == 4) {
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != dxOld) { // vx just stopped
+ if (dy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != dxOld) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+
+ if (obj->vx || obj->vy) {
+ if (obj->seqNumb > 1)
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ obj->cycling = NOT_CYCLING;
+ _vm->boundaryCollision(obj); // Must have got hero!
+ }
+ dxOld = obj->vx;
+ dyOld = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER:
+ if (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_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 > 2)) {
+ if (obj->vx != dxOld) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != dxOld) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ else
+ obj->cycling = NOT_CYCLING;
+ }
+ dxOld = obj->vx;
+ dyOld = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_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))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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)
+ _vm->processMaze();
+}
+
+void ObjectHandler_v1d::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, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ _objects[objNumb1].currImagePtr = _objects[objNumb1].seqList[0].seqPtr;
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v1w.cpp b/engines/hugo/object_v1w.cpp
new file mode 100644
index 0000000000..05fa4bd35c
--- /dev/null
+++ b/engines/hugo/object_v1w.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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+
+namespace Hugo {
+
+ObjectHandler_v1w::ObjectHandler_v1w(HugoEngine *vm) : ObjectHandler(vm) {
+}
+
+ObjectHandler_v1w::~ObjectHandler_v1w() {
+}
+
+// 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 ObjectHandler_v1w::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_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:
+ _vm->_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
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ else
+ _vm->_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;
+ }
+ _vm->_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;
+ }
+ }
+}
+
+// Update all object positions. Process object 'local' events
+// including boundary events and collisions
+void ObjectHandler_v1w::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_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 = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_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;
+ _vm->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 (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_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))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_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))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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)
+ _vm->processMaze();
+}
+
+void ObjectHandler_v1w::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, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ saveSeq(&_objects[objNumb1]);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ restoreSeq(&_objects[objNumb1]);
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+
+ // Make sure baseline stays constant
+ _objects[objNumb1].y += _objects[objNumb2].currImagePtr->y2 - _objects[objNumb1].currImagePtr->y2;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v2d.cpp b/engines/hugo/object_v2d.cpp
new file mode 100644
index 0000000000..3a0e367261
--- /dev/null
+++ b/engines/hugo/object_v2d.cpp
@@ -0,0 +1,352 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+ObjectHandler_v2d::ObjectHandler_v2d(HugoEngine *vm) : ObjectHandler_v1d(vm) {
+}
+
+ObjectHandler_v2d::~ObjectHandler_v2d() {
+}
+
+// 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 ObjectHandler_v2d::updateImages() {
+ debugC(5, kDebugObject, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAXOBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_vm->_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:
+ _vm->_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
+ _vm->_screen->displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ else
+ _vm->_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;
+ }
+ _vm->_screen->displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ _vm->_scheduler->waitForRefresh();
+
+ // 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;
+ }
+ }
+}
+
+// Update all object positions. Process object 'local' events
+// including boundary events and collisions
+void ObjectHandler_v2d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_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 = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_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;
+ _vm->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 (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_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))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_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))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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)
+ _vm->processMaze();
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/object_v3d.cpp b/engines/hugo/object_v3d.cpp
new file mode 100644
index 0000000000..42476ce379
--- /dev/null
+++ b/engines/hugo/object_v3d.cpp
@@ -0,0 +1,253 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/random.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/object.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/parser.h"
+
+namespace Hugo {
+
+ObjectHandler_v3d::ObjectHandler_v3d(HugoEngine *vm) : ObjectHandler_v2d(vm) {
+}
+
+ObjectHandler_v3d::~ObjectHandler_v3d() {
+}
+
+// Update all object positions. Process object 'local' events
+// including boundary events and collisions
+void ObjectHandler_v3d::moveObjects() {
+ debugC(4, kDebugObject, "moveObjects");
+
+ // Added to DOS version in order to handle mouse properly
+ // If route mode enabled, do special route processing
+ if (_vm->getGameStatus().routeIndex >= 0)
+ _vm->_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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_vm->_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 = _vm->_hero->x + _vm->_hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _vm->_hero->y + _vm->_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;
+ _vm->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 (!_vm->_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _vm->_rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _vm->_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))
+ _vm->storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_vm->_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))
+ _vm->clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = _vm->deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = _vm->deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ _vm->boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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 < _vm->_numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_vm->_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ _vm->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)
+ _vm->processMaze();
+}
+
+void ObjectHandler_v3d::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, kDebugObject, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ saveSeq(&_objects[objNumb1]);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _objects[objNumb1].seqList, seqListSize);
+ memcpy(_objects[objNumb1].seqList, _objects[objNumb2].seqList, seqListSize);
+ memcpy(_objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ restoreSeq(&_objects[objNumb1]);
+ _objects[objNumb2].currImagePtr = _objects[objNumb2].seqList[0].seqPtr;
+ _vm->_heroImage = (_vm->_heroImage == HERO) ? objNumb2 : HERO;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp
index 33fd0ddfef..a1f493706e 100644
--- a/engines/hugo/parser.cpp
+++ b/engines/hugo/parser.cpp
@@ -30,21 +30,16 @@
*
*/
-// parser.c - handles all keyboard/command input
-
#include "common/system.h"
-#include "common/keyboard.h"
-#include "hugo/game.h"
#include "hugo/hugo.h"
#include "hugo/parser.h"
-#include "hugo/global.h"
#include "hugo/file.h"
-#include "hugo/schedule.h"
#include "hugo/display.h"
#include "hugo/route.h"
#include "hugo/util.h"
#include "hugo/sound.h"
+#include "hugo/object.h"
namespace Hugo {
@@ -52,16 +47,19 @@ namespace Hugo {
#define CX(X) LOWORD(X)
#define CY(Y) HIWORD(Y)
-Parser::Parser(HugoEngine &vm) :
+Parser::Parser(HugoEngine *vm) :
_vm(vm), _putIndex(0), _getIndex(0), _checkDoubleF1Fl(false) {
}
-void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
- status_t &gameStatus = _vm.getGameStatus();
- bool repeatedFl = (nFlags & 0x4000); // TRUE if key is a repeat
+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
@@ -77,14 +75,14 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
case Common::KEYCODE_DOWN:
if (!repeatedFl) {
gameStatus.routeIndex = -1; // Stop any automatic route
- _vm.route().setWalk(nChar); // Direction of hero travel
+ _vm->_route->setWalk(nChar); // Direction of hero travel
}
break;
case Common::KEYCODE_F1: // User Help (DOS)
if (_checkDoubleF1Fl)
- _vm.file().instructions();
+ _vm->_file->instructions();
else
- _vm.screen().userHelp();
+ _vm->_screen->userHelp();
_checkDoubleF1Fl = !_checkDoubleF1Fl;
break;
case Common::KEYCODE_F6: // Inventory
@@ -94,14 +92,16 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
_config.turboFl = !_config.turboFl;
break;
case Common::KEYCODE_F2: // Toggle sound
- _vm.sound().toggleSound();
- _vm.sound().toggleMusic();
+ _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() - F3-F9 (DOS)");
+ warning("STUB: KeyHandler() - F4-F5-F9 (DOS)");
break;
default: // Any other key
if (!gameStatus.storyModeFl) { // Keyboard disabled
@@ -122,23 +122,19 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
}
// Add any new chars to line buffer and display them.
-// If CR pressed, pass line to Line_handler()
+// 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 = '_';
- char c;
static command_t cmdLine; // Build command line
- status_t &gameStatus = _vm.getGameStatus();
-// Strangerke : Useless ?
-// bool updateFl = (_getIndex != _putIndex); // TRUE if any chars processed
-// command_t status_line; // Includes prompt, cursor
-
- debugC(4, kDebugParser, "charHandler");
+ status_t &gameStatus = _vm->getGameStatus();
// Check for one or more characters in ring buffer
while (_getIndex != _putIndex) {
- c = _ringBuffer[_getIndex++];
+ char c = _ringBuffer[_getIndex++];
if (_getIndex >= sizeof(_ringBuffer))
_getIndex = 0;
@@ -148,7 +144,7 @@ void Parser::charHandler() {
cmdLine[--lineIndex] = '\0';
break;
case Common::KEYCODE_RETURN: // EOL, pass line to line handler
- if (lineIndex && (_vm._hero->pathType != QUIET)) {
+ if (lineIndex && (_vm->_hero->pathType != QUIET)) {
// Remove inventory bar if active
if (gameStatus.inventoryState == I_ACTIVE)
gameStatus.inventoryState = I_UP;
@@ -170,11 +166,8 @@ void Parser::charHandler() {
}
// See if time to blink cursor, set cursor character
- if ((tick++ % (TPS / BLINKS)) == 0) {
-// Strangerke : Useless ?
-// updateFl = true; // Force an update
+ if ((tick++ % (TPS / BLINKS)) == 0)
cursor = (cursor == '_') ? ' ' : '_';
- }
// See if recall button pressed
if (gameStatus.recallFl) {
@@ -184,8 +177,8 @@ void Parser::charHandler() {
lineIndex = strlen(cmdLine);
}
- sprintf(_statusLine, ">%s%c", cmdLine, cursor);
- sprintf(_scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_config.turboFl) ? "T" : " ", _vm.getScore(), _vm.getMaxScore(), (_config.soundFl) ? "On" : "Off");
+ 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) {
@@ -194,32 +187,12 @@ void Parser::charHandler() {
}
}
-void Parser::drawStatusText() {
- debugC(4, kDebugParser, "drawStatusText");
-
- if (_vm.getPlatform() == Common::kPlatformWindows)
- _vm.screen().loadFont(U_FONT8);
- uint16 sdx = _vm.screen().stringLength(_statusLine);
- uint16 sdy = _vm.screen().fontHeight() + 1; // + 1 for shadow
- uint16 posX = 0;
- uint16 posY = YPIX - sdy;
- // Display the string and add rect to display list
- _vm.screen().writeStr(posX, posY, _statusLine, _TLIGHTYELLOW);
- _vm.screen().displayList(D_ADD, posX, posY, sdx, sdy);
-
- sdx = _vm.screen().stringLength(_scoreLine);
- posY = 0;
- _vm.screen().writeStr(posX, posY, _scoreLine, _TCYAN);
- _vm.screen().displayList(D_ADD, posX, posY, sdx, sdy);
-}
-
// Perform an immediate command. Takes parameters a la sprintf
// Assumes final string will not overrun line[] length
void Parser::command(const char *format, ...) {
- va_list marker;
-
debugC(1, kDebugParser, "Command(%s, ...)", format);
+ va_list marker;
va_start(marker, format);
vsprintf(_line, format, marker);
va_end(marker);
@@ -227,492 +200,80 @@ void Parser::command(const char *format, ...) {
lineHandler();
}
-char *Parser::strlwr(char *buffer) {
- char *result = buffer;
-
- debugC(1, kDebugParser, "strlwr(%s)", buffer);
-
- while (*buffer != '\0') {
- if (isupper(*buffer))
- *buffer = tolower(*buffer);
- buffer++;
- }
-
- return result;
-}
-
-// Parse the user's line of text input. Generate events as necessary
-void Parser::lineHandler() {
- char *noun, *verb; // ptrs to noun and verb strings
-// int i;
- object_t *obj;
- char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
- char contextComment[XBYTES * 5] = ""; // Unused comment for context objects
- status_t &gameStatus = _vm.getGameStatus();
-
-
- debugC(1, kDebugParser, "lineHandler");
-
- // Toggle God Mode
- if (!strncmp(_line, "PPG", 3)) {
- _vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI);
- gameStatus.godModeFl ^= 1;
- return;
- }
-
- 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 (DEBUG || 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;
- }
-
- // Test for nearby objects referenced explicitly
- for (int i = 0; i < _vm._numObj; i++) {
- obj = &_vm._objects[i];
- if (isWordPresent(_vm._arrayNouns[obj->nounIndex]))
- if (isObjectVerb(obj, _line, farComment) || isGenericVerb(obj, _line, 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++) {
- obj = &_vm._objects[i];
- if (obj->verbOnlyFl)
- if (isObjectVerb(obj, _line, contextComment) || isGenericVerb(obj, _line, contextComment))
- return;
- }
-
- // No objects match command line, try background and catchall commands
- if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p], _line))
- return;
- if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p], _line))
- return;
- if (isBackgroundWord(_vm._catchallList, _line))
- return;
- if (isCatchallVerb(_vm._catchallList, _line))
- 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.
- verb = findVerb(_line);
- noun = findNoun(_line);
- 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]);
-}
-
-// Search for matching verb/noun pairs in background command list
-// Print text for possible background object. Return TRUE if match found
-bool Parser::isBackgroundWord(objectList_t obj, char *line) {
- debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj, %s)", line);
-
- 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;
-}
-
-// 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::isCatchallVerb(objectList_t obj, char *line) {
- debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj, %s)", line);
-
- for (int i = 0; obj[i].verbIndex != 0; i++)
- if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 &&
- (!obj[i].matchFl || !findNoun(line)) &&
- ((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;
-}
-
-// 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::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;
-}
-
// 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 != NULL) {
- for (int i = 0; strlen(wordArr[i]); i++)
+ if (wordArr != 0) {
+ for (int i = 0; strlen(wordArr[i]); i++) {
if (strstr(_line, wordArr[i]))
- return(true);
+ return true;
+ }
}
-
return false;
}
// Locate word in list of nouns and return ptr to first string in noun list
-char *Parser::findNoun(char *line) {
- debugC(1, kDebugParser, "findNoun(%s)", line);
-
- 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 NULL;
-}
-
-// Locate word in list of verbs and return ptr to first string in verb list
-char *Parser::findVerb(char *line) {
- debugC(1, kDebugParser, "findVerb(%s)", line);
-
- 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 NULL;
-}
-
-// Describe any takeable objects visible in this screen
-void Parser::showTakeables() {
- object_t *obj;
-
- debugC(1, kDebugParser, "showTakeables");
+char *Parser::findNoun() {
+ debugC(1, kDebugParser, "findNoun()");
- for (int j = 0; j < _vm._numObj; j++) {
- obj = &_vm._objects[j];
- if ((obj->cycling != INVISIBLE) &&
- (obj->screenIndex == *_vm._screen_p) &&
- (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) {
-// sprintf(_textBoxBuffer, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]);
- Utils::Box(BOX_ANY, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]);
+ 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;
}
-// Do all things necessary to carry an object
-void Parser::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;
- if (_vm.getPlatform() != Common::kPlatformWindows)
- warning("takeObject : DOS version should use ALMOST_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::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]);
-}
+// Locate word in list of verbs and return ptr to first string in verb list
+char *Parser::findVerb() {
+ debugC(1, kDebugParser, "findVerb()");
-// Test whether command line contains one of the generic actions
-bool Parser::isGenericVerb(object_t *obj, char *line, char *comment) {
- debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s, %s)", line, 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]);
+ 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];
}
- } 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;
-}
-
-// 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;
-}
-
-// 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::isObjectVerb(object_t *obj, char *line, char *comment) {
- int i;
- cmd *cmnd;
- char *verb;
- uint16 *reqs;
- uint16 cmdIndex;
-
- debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s, %s)", line, comment);
-
- // First, find matching verb in cmd list
- cmdIndex = obj->cmdIndex; // ptr to list of commands
- if (cmdIndex == 0) // No commands for this obj
- return false;
-
- 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
- verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex];
- if (!isNear(obj, verb, comment))
- return(false);
-
- // Check all required objects are being carried
- cmnd = &_vm._cmdList[cmdIndex][i]; // ptr to struct cmd
- if (cmnd->reqIndex) { // At least 1 thing in list
- 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, line, comment);
- return true;
+ return 0;
}
-void Parser::showDosInventory() {
// 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, len, len1 = 0, len2 = 0;
- char buffer[XBYTES *NUM_ROWS] = "\0";
+ uint16 index = 0, len1 = 0, len2 = 0;
- index = 0;
- for (int i = 0; i < _vm._numObj; i++) /* Find widths of 2 columns */
- if (_vm._objects[i].carriedFl) {
- len = strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]);
- if (index++ & 1) /* Right hand column */
+ for (int i = 0; i < _vm->_numObj; i++) { // Find widths of 2 columns
+ if (_vm->_object->isCarried(i)) {
+ uint16 len = strlen(_vm->_arrayNouns[_vm->_object->_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 */
+ }
+ len1 += 1; // For gap between columns
- if (len1 + len2 < (uint16)strlen(_vm._textParser[kTBOutro]))
- len1 = strlen(_vm._textParser[kTBOutro]);
+ if (len1 + len2 < (uint16)strlen(_vm->_textParser[kTBOutro]))
+ len1 = strlen(_vm->_textParser[kTBOutro]);
- strncat(buffer, blanks, (len1 + len2 - strlen(_vm._textParser[kTBIntro])) / 2);
- strcat(strcat(buffer, _vm._textParser[kTBIntro]), "\n");
+ 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) {
+ for (int i = 0; i < _vm->_numObj; i++) { // Assign strings
+ if (_vm->_object->isCarried(i)) {
if (index++ & 1)
- strcat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), "\n");
+ strcat(strcat(buffer, _vm->_arrayNouns[_vm->_object->_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]));
+ strncat(strcat(buffer, _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]), blanks, len1 - strlen(_vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][1]));
}
}
if (index & 1)
strcat(buffer, "\n");
- strcat(buffer, _vm._textParser[kTBOutro]);
+ strcat(buffer, _vm->_textParser[kTBOutro]);
Utils::Box(BOX_ANY, "%s", buffer);
}
diff --git a/engines/hugo/parser.h b/engines/hugo/parser.h
index cc94ff7adc..b98813c7bc 100644
--- a/engines/hugo/parser.h
+++ b/engines/hugo/parser.h
@@ -35,52 +35,94 @@
namespace Hugo {
enum seqTextParser {
- kTBExit = 0, kTBMaze, kTBNoPoint, kTBNoun, kTBVerb,
- kTBEh, kTBUnusual, kTBHave, kTBNoUse, kTBDontHave,
- kTBNeed, kTBOk, kCmtAny1, kCmtAny2, kCmtAny3,
- kCmtClose, kTBIntro, kTBOutro
+ 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);
+ Parser(HugoEngine *vm);
+ virtual ~Parser();
bool isWordPresent(char **wordArr);
void charHandler();
void command(const char *format, ...);
- void drawStatusText();
void keyHandler(uint16 nChar, uint16 nFlags);
- void lineHandler();
+ virtual void lineHandler() = 0;
-private:
- HugoEngine &_vm;
+protected:
+ HugoEngine *_vm;
+
+protected:
+ char *findNoun();
+ char *findVerb();
+private:
char _ringBuffer[32]; // Ring buffer
uint16 _putIndex;
uint16 _getIndex; // Index into ring buffer
bool _checkDoubleF1Fl; // Flag used to display user help or instructions
- command_t _statusLine;
- command_t _scoreLine;
+ void showDosInventory();
+};
- bool isBackgroundWord(objectList_t obj, char *line);
- bool isCarrying(uint16 wordIndex);
- bool isCatchallVerb(objectList_t obj, char *line);
- bool isGenericVerb(object_t *obj, char *line, char *comment);
- bool isNear(object_t *obj, char *verb, char *comment);
- bool isObjectVerb(object_t *obj, char *line, char *comment);
+class Parser_v1w : public Parser {
+public:
+ Parser_v1w(HugoEngine *vm);
+ ~Parser_v1w();
+
+ virtual void lineHandler();
- char *findNoun(char *line);
- char *findVerb(char *line);
- char *strlwr(char *buffer);
+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 showDosInventory();
- void showTakeables();
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..dcdc2c74ac
--- /dev/null
+++ b/engines/hugo/parser_v1d.cpp
@@ -0,0 +1,356 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/object.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 (!_vm->_object->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->_object->_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..5021db4c6e
--- /dev/null
+++ b/engines/hugo/parser_v1w.cpp
@@ -0,0 +1,435 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.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 (!_vm->_object->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])
+ _vm->_object->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->_object->_objects[i].genericCmd & TAKE)
+ takeObject(&_vm->_object->_objects[i]);
+ }
+ return;
+ }
+
+ if (strstr(_line, "fetch")) {
+ for (int i = 0; i < _vm->_numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ takeObject(&_vm->_object->_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->_object->_objects[i].nounIndex][0])) {
+ _vm->_scheduler->newScreen(_vm->_object->_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->_object->_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->_object->_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]);
+ _vm->_object->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..99fcf63a4e
--- /dev/null
+++ b/engines/hugo/parser_v2d.cpp
@@ -0,0 +1,138 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.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->_object->_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]);
+ _vm->_object->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..49c9ec2c08
--- /dev/null
+++ b/engines/hugo/parser_v3d.cpp
@@ -0,0 +1,204 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.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->_object->_objects[i].genericCmd & TAKE)
+ takeObject(&_vm->_object->_objects[i]);
+ }
+ return;
+ }
+
+ if (strstr(_line, "fetch")) {
+ for (int i = 0; i < _vm->_numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm->_arrayNouns[_vm->_object->_objects[i].nounIndex][0])) {
+ takeObject(&_vm->_object->_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->_object->_objects[i].nounIndex][0])) {
+ _vm->_scheduler->newScreen(_vm->_object->_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->_object->_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->_object->_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
index deb4dc27d5..9e068171a2 100644
--- a/engines/hugo/route.cpp
+++ b/engines/hugo/route.cpp
@@ -38,17 +38,18 @@
#include "hugo/game.h"
#include "hugo/route.h"
#include "hugo/global.h"
+#include "hugo/object.h"
namespace Hugo {
-Route::Route(HugoEngine &vm) : _vm(vm) {
+Route::Route(HugoEngine *vm) : _vm(vm) {
}
// Face hero in new direction, based on cursor key input by user.
void Route::setDirection(uint16 keyCode) {
- object_t *obj = _vm._hero; // Pointer to hero object
-
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:
@@ -69,24 +70,24 @@ void Route::setDirection(uint16 keyCode) {
case Common::KEYCODE_END:
obj->currImagePtr = obj->seqList[LEFT].seqPtr;
break;
-// case Common::KEYCODE_PRIOR:
-// obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
-// break;
-// case Common::KEYCODE_NEXT:
-// obj->currImagePtr = obj->seqList[RIGHT].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) {
- object_t *obj = _vm._hero; // Pointer to hero object
- static uint16 oldDirection = 0; // Last direction char
-
debugC(1, kDebugRoute, "setWalk(%d)", direction);
- if (_vm.getGameStatus().storyModeFl || obj->pathType != USER) // Make sure user has control
+ 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)
@@ -111,20 +112,24 @@ void Route::setWalk(uint16 direction) {
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;
-// case Common::KEYCODE_PRIOR:
-// obj->vx = DX;
-// obj->vy = -DY / 2;
-// break;
-// case Common::KEYCODE_NEXT:
-// obj->vx = DX;
-// obj->vy = DY / 2;
-// break;
}
oldDirection = direction;
obj->cycling = CYCLE_FORWARD;
@@ -148,13 +153,12 @@ void Route::setWalk(uint16 direction) {
// 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) {
- int16 x1, x2; // Range of segment
+ 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
- debugC(1, kDebugRoute, "segment(%d, %d)", x, y);
-
// Bomb out if stack exhausted
// Vinterstum: Is this just a safeguard, or actually used?
//_fullStackFl = _stackavail () < 256;
@@ -162,16 +166,21 @@ void Route::segment(int16 x, int16 y) {
// Find and fill on either side of point
p = _boundaryMap[y];
- for (x1 = x; x1 > 0; x1--)
+ int16 x1, x2; // Range of segment
+ for (x1 = x; x1 > 0; x1--) {
if (p[x1] == 0) {
p[x1] = kMapFill;
- } else
+ } else {
break;
- for (x2 = x + 1; x2 < XPIX; x2++)
+ }
+ }
+ for (x2 = x + 1; x2 < XPIX; x2++) {
if (p[x2] == 0) {
p[x2] = kMapFill;
- } else
+ } else {
break;
+ }
+ }
x1++;
x2--;
@@ -187,51 +196,62 @@ void Route::segment(int16 x, int16 y) {
if (y <= 0 || y >= YPIX - 1)
return;
- if (_vm._hero->x < x1) {
+ 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++)
+ 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++)
+ 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) {
+ }
+ } 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--)
+ 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--)
+ 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++)
+ 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++)
+ }
+
+ 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++)
+ }
+
+ 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++)
+ }
+
+ 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)
+ if (_segmentNumb >= kMaxSeg - 1) {
_fullSegmentFl = true;
- else {
+ } else {
// Create segment
seg_p = &_segment[_segmentNumb];
seg_p->y = y;
@@ -243,15 +263,15 @@ void Route::segment(int16 x, int16 y) {
}
// Create and return ptr to new node. Initialize with previous node.
-// Returns NULL if MAX_NODES exceeded
+// Returns 0 if MAX_NODES exceeded
Point *Route::newNode() {
debugC(1, kDebugRoute, "newNode");
if (_routeListIndex >= kMaxNodes) // Too many nodes
- return(NULL); // Incomplete route - failure
+ return 0; // Incomplete route - failure
_routeListIndex++;
_route[_routeListIndex] = _route[_routeListIndex - 1]; // Initialize with previous node
- return(&_route[_routeListIndex]);
+ return &_route[_routeListIndex];
}
// Construct route to cx, cy. Return TRUE if successful.
@@ -259,13 +279,6 @@ Point *Route::newNode() {
// 2. Construct list of segments segment[] from hero to destination
// 3. Compress to shortest route in route[]
bool Route::findRoute(int16 cx, int16 cy) {
- int16 i, j, x, y; // Loop on coordinates
- int16 x1, x2, dx; // Overlap between segments
- int16 herox1, herox2, heroy; // Current hero baseline
- object_t *obj; // Ptr to object
- segment_t *seg_p; // Ptr to segment
- Point *routeNode; // Ptr to route node
-
debugC(1, kDebugRoute, "findRoute(%d, %d)", cx, cy);
// Initialize for search
@@ -276,32 +289,39 @@ bool Route::findRoute(int16 cx, int16 cy) {
_heroWidth = HERO_MIN_WIDTH; // Minimum width of hero
_destY = cy; // Destination coords
_destX = cx; // Destination coords
- herox1 = _vm._hero->x + _vm._hero->currImagePtr->x1; // Hero baseline
- herox2 = _vm._hero->x + _vm._hero->currImagePtr->x2; // Hero baseline
- heroy = _vm._hero->y + _vm._hero->currImagePtr->y2; // Hero baseline
+
+ 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])
- 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);
+ object_t *obj; // Ptr to object
+ int i;
+ for (i = 1, obj = &_vm->_object->_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 (y = 0; y < YPIX; y++)
- for (x = 0; x < XBYTES; x++)
+ 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;
+ _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);
+ for (i = 0, obj = _vm->_object->_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);
+ return false;
}
// Now find the route of nodes from destination back to hero
@@ -315,29 +335,30 @@ bool Route::findRoute(int16 cx, int16 cy) {
_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()) == NULL) // New node for new segment
- return(false); // Too many nodes
+ 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 (j = i + 1; j < _segmentNumb; j++) {
- seg_p = &_segment[j];
+ 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)
+ if (seg_p->x1 <= routeNode->x && seg_p->x2 >= routeNode->x + _heroWidth - 1) {
routeNode->y = seg_p->y; // Yes, keep updating node
- else {
+ } else {
// No, create another node on previous segment to reach it
- if ((routeNode = newNode()) == NULL) // Add new route node
- return (false); // Too many nodes
+ if ((routeNode = newNode()) == 0) // Add new route node
+ return false; // Too many nodes
// Find overlap between old and new segments
- x1 = MAX(_segment[j - 1].x1, seg_p->x1);
- x2 = MIN(_segment[j - 1].x2, seg_p->x2);
+ 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
- dx = HERO_MAX_WIDTH >> 1;
+ int16 dx = HERO_MAX_WIDTH >> 1;
if (x2 - x1 < _heroWidth + dx)
dx = 0;
@@ -364,51 +385,48 @@ bool Route::findRoute(int16 cx, int16 cy) {
// Process hero in route mode - called from Move_objects()
void Route::processRoute() {
- int16 herox, heroy; // Hero position
- Point *routeNode; // Ptr to current route node
- static bool turnedFl = false; // Used to get extra cylce for turning
-
- status_t &gameStatus = _vm.getGameStatus();
-
debugC(1, kDebugRoute, "processRoute");
+ static bool turnedFl = false; // Used to get extra cylce for turning
+
// Current hero position
- herox = _vm._hero->x + _vm._hero->currImagePtr->x1;
- heroy = _vm._hero->y + _vm._hero->currImagePtr->y2;
- routeNode = &_route[gameStatus.routeIndex];
+ 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;
+ _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);
+ setWalk(_vm->_hotspots[gameStatus.go_id].direction);
break;
case GO_LOOK: // Look at an object
if (turnedFl) {
- _vm.lookObject(&_vm._objects[gameStatus.go_id]);
+ _vm->_object->lookObject(&_vm->_object->_objects[gameStatus.go_id]);
turnedFl = false;
} else {
- setDirection(_vm._objects[gameStatus.go_id].direction);
+ setDirection(_vm->_object->_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);
+ _vm->_object->useObject(gameStatus.go_id);
turnedFl = false;
} else {
- setDirection(_vm._objects[gameStatus.go_id].direction);
+ setDirection(_vm->_object->_objects[gameStatus.go_id].direction);
gameStatus.routeIndex++; // Come round again
turnedFl = true;
}
@@ -418,20 +436,20 @@ void Route::processRoute() {
break;
}
}
- } else if (_vm._hero->vx == 0 && _vm._hero->vy == 0) {
+ } 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)
+ if (herox < routeNode->x) {
setWalk(Common::KEYCODE_RIGHT);
- else if (herox > routeNode->x)
+ } else if (herox > routeNode->x) {
setWalk(Common::KEYCODE_LEFT);
- else if (heroy < routeNode->y) {
+ } else if (heroy < routeNode->y) {
setWalk(Common::KEYCODE_DOWN);
- _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ _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;
+ _vm->_hero->x = _vm->_hero->oldx = routeNode->x - _vm->_hero->currImagePtr->x1;
}
}
}
@@ -440,16 +458,13 @@ void Route::processRoute() {
// 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) {
- bool foundFl = false; // TRUE if route found ok
-
- status_t &gameStatus = _vm.getGameStatus();
-
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)
+ 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;
@@ -461,9 +476,10 @@ bool Route::startRoute(go_t go_for, int16 id, int16 cx, int16 cy) {
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
+ _vm->_hero->vx = _vm->_hero->vy = 0; // Stop manual motion
}
return foundFl;
diff --git a/engines/hugo/route.h b/engines/hugo/route.h
index 09b4575fcd..b2185a4bb7 100644
--- a/engines/hugo/route.h
+++ b/engines/hugo/route.h
@@ -53,7 +53,7 @@ struct segment_t { // Search segment
class Route {
public:
- Route(HugoEngine &vm);
+ Route(HugoEngine *vm);
void processRoute();
bool startRoute(go_t go_for, short id, short cx, short cy);
@@ -61,7 +61,7 @@ public:
void setWalk(uint16 direction);
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
byte _boundaryMap[YPIX][XPIX]; // Boundary byte map
segment_t _segment[kMaxSeg]; // List of points in fill-path
diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp
index 41d194120f..a72b337ffe 100644
--- a/engines/hugo/schedule.cpp
+++ b/engines/hugo/schedule.cpp
@@ -33,23 +33,16 @@
// This module contains all the scheduling and timing stuff
#include "common/system.h"
-#include "common/stream.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(HugoEngine *vm) : _vm(vm) {
}
Scheduler::~Scheduler() {
@@ -76,453 +69,77 @@ void Scheduler::initEventQueue() {
// Return a ptr to an event structure from the free list
event_t *Scheduler::getQueue() {
debugC(4, kDebugSchedule, "getQueue");
- event_t *resEvent;
if (!_freeEvent) // Error: no more events available
Utils::Error(EVNT_ERR, "%s", "getQueue");
- resEvent = _freeEvent;
+ 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 = NULL;
- } 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 = NULL;
- _headEvent = curEvent;
- }
- }
-}
-
-void Scheduler::insertActionList(uint16 actIndex) {
// Call Insert_action for each action in the list supplied
+void Scheduler::insertActionList(uint16 actIndex) {
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]);
+ 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
+void Scheduler::decodeString(char *line) {
debugC(1, kDebugSchedule, "decodeString(%s)", line);
- const char *cypher = getCypher();
+ 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
- event_t *wrkEvent; // Save ev_p->next_p for return
- event_t *saveEvent; // Used in DEL_EVENTS
- char *response; // User's response string
- object_t *obj1;
- object_t *obj2;
- int dx, dy;
- act *action; // Ptr to action structure
-
- status_t &gameStatus = _vm.getGameStatus();
-
- action = curEvent->action;
- debugC(1, kDebugSchedule, "doAction - Event action type : %d", action->a0.actType);
-
- 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(_vm.parser().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)) != NULL)
- 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;
- }
+// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
+uint32 Scheduler::getWinTicks() {
+ debugC(3, kDebugSchedule, "getTicks");
- if (action->a0.actType == NEW_SCREEN) // New_screen() deletes entire list
- return (NULL); // next_p = NULL since list now empty
- else {
- wrkEvent = curEvent->nextEvent;
- delQueue(curEvent); // Return event to free list
- return(wrkEvent); // Return next event ptr
- }
+ return _vm->getGameStatus().tick;
}
-// 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");
+// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
+// If update FALSE, simply return last known time
+// Note that this is real time unless a processing cycle takes longer than
+// a real tick, in which case the system tick is simply incremented
+uint32 Scheduler::getDosTicks(bool updateFl) {
+ debugC(5, kDebugSchedule, "getTicks");
- status_t &gameStatus = _vm.getGameStatus();
+ static uint32 tick = 0; // Current system time in ticks
+ static uint32 t_old = 0; // The previous wall time in ticks
- 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 t_now; // Current wall time in ticks
-uint32 Scheduler::getTicks() {
-// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
- debugC(3, kDebugSchedule, "getTicks");
+ if (!updateFl)
+ return(tick);
- return _vm.getGameStatus().tick;
+ if (t_old == 0)
+ t_old = (uint32) floor((double) (g_system->getMillis() * TPS / 1000));
+ /* Calculate current wall time in ticks */
+ t_now = g_system->getMillis() * TPS / 1000 ;
+
+ if ((t_now - t_old) > 0) {
+ t_old = t_now;
+ tick++;
+ }
+ return(tick);
}
-void Scheduler::processBonus(int bonusIndex) {
// Add indecated bonus to score if not added already
+void Scheduler::processBonus(int bonusIndex) {
debugC(1, kDebugSchedule, "processBonus(%d)", bonusIndex);
- if (!_vm._points[bonusIndex].scoredFl) {
- _vm.adjustScore(_vm._points[bonusIndex].score);
- _vm._points[bonusIndex].scoredFl = true;
+ if (!_vm->_points[bonusIndex].scoredFl) {
+ _vm->adjustScore(_vm->_points[bonusIndex].score);
+ _vm->_points[bonusIndex].scoredFl = true;
}
}
@@ -536,11 +153,11 @@ void Scheduler::newScreen(int screenIndex) {
debugC(1, kDebugSchedule, "newScreen(%d)", screenIndex);
// Make sure the background file exists!
- if (!_vm.isPacked()) {
+ 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]);
+ 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;
}
}
@@ -556,145 +173,51 @@ void Scheduler::newScreen(int screenIndex) {
}
// 2. Set the new screen in the hero object and any being carried
- _vm.setNewScreen(screenIndex);
+ _vm->setNewScreen(screenIndex);
// 3. Read in new screen files
- _vm.readScreenFiles(screenIndex);
+ _vm->readScreenFiles(screenIndex);
// 4. Schedule action list for this screen
- _vm.screenActions(screenIndex);
+ _vm->screenActions(screenIndex);
// 5. Initialise prompt line and status line
- _vm.initNewScreenDisplay();
+ _vm->_screen->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) {
- uint32 curTime;
- event_t saveEvents_[kMaxEvents]; // Convert event ptrs to indexes
- event_t *wrkEvent; // Event ptr
- int16 freeIndex; // Free list index
- int16 headIndex; // Head of list index
- int16 tailIndex; // Tail of list index
-
- debugC(1, kDebugSchedule, "saveEvents");
-
- curTime = getTicks();
-
- // Convert event ptrs to indexes
- for (int16 i = 0; i < kMaxEvents; i++) {
- wrkEvent = &_events[i];
- saveEvents_[i] = *wrkEvent;
- saveEvents_[i].prevEvent = (wrkEvent->prevEvent == NULL) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events);
- saveEvents_[i].nextEvent = (wrkEvent->nextEvent == NULL) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events);
- }
- freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events;
- headIndex = (_headEvent == 0) ? -1 : _headEvent - _events;
- 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(saveEvents_, sizeof(saveEvents_));
-}
-
-// Restore the event list from file with handle f
-void Scheduler::restoreEvents(Common::SeekableReadStream *f) {
- uint32 curTime, saveTime;
- event_t *wrkEvent; // Event ptr
- event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes
- int16 freeIndex; // Free list index
- int16 headIndex; // Head of list index
- int16 tailIndex; // Tail of list index
-
- debugC(1, kDebugSchedule, "restoreEvents");
-
- 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));
-
- // 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) ? NULL : &_events[freeIndex];
- _headEvent = (headIndex == -1) ? NULL : &_events[headIndex];
- _tailEvent = (tailIndex == -1) ? NULL : &_events[tailIndex];
-
- // Adjust times to fit our time
- 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
-
+void Scheduler::restoreScreen(int screenIndex) {
debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex);
// 1. Set the new screen in the hero object and any being carried
- _vm.setNewScreen(screenIndex);
+ _vm->setNewScreen(screenIndex);
// 2. Read in new screen files
- _vm.readScreenFiles(screenIndex);
+ _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
- seqList_t tmpSeqList[MAX_SEQUENCES];
- int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
-
- debugC(1, kDebugSchedule, "swapImages(%d, %d)", objNumb1, objNumb2);
-
- _vm.file().saveSeq(&_vm._objects[objNumb1]);
- 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;
+ _vm->_screen->initNewScreenDisplay();
}
-Scheduler_v1::Scheduler_v1(HugoEngine &vm) : Scheduler(vm) {
-}
+// Wait (if necessary) for next synchronizing tick
+// Slow machines won't make it by the end of tick, so will just plod on
+// at their own speed, not waiting here, but free running.
+// Note: DOS Versions only
+void Scheduler::waitForRefresh(void) {
+ debugC(1, kDebugSchedule, "waitForRefresh()");
-Scheduler_v1::~Scheduler_v1() {
-}
+ static uint32 timeout = 0;
+ uint32 t;
-const char *Scheduler_v1::getCypher() {
- return "Copyright 1991, Gray Design Associates";
-}
+ if (timeout == 0)
+ timeout = getDosTicks(true);
-Scheduler_v2::Scheduler_v2(HugoEngine &vm) : Scheduler(vm) {
+ while ((t = getDosTicks(true)) < timeout)
+ ;
+ timeout = ++t;
}
-Scheduler_v2::~Scheduler_v2() {
-}
-
-const char *Scheduler_v2::getCypher() {
- return "Copyright 1992, Gray Design Associates";
-}
} // End of namespace Hugo
diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h
index 285f7bd663..e6eb5d7947 100644
--- a/engines/hugo/schedule.h
+++ b/engines/hugo/schedule.h
@@ -35,71 +35,104 @@
namespace Hugo {
-#define kMaxEvents 50 /* Max events in event queue */
+#define SIGN(X) ((X < 0) ? -1 : 1)
+#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 */
+ 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);
+ Scheduler(HugoEngine *vm);
virtual ~Scheduler();
+ virtual void insertAction(act *action) = 0;
+ virtual void restoreEvents(Common::SeekableReadStream *f) = 0;
+ virtual void runScheduler() = 0;
+ virtual void saveEvents(Common::WriteStream *f) = 0;
+
void initEventQueue();
- void insertAction(act *action);
void insertActionList(uint16 actIndex);
void decodeString(char *line);
- void runScheduler();
- uint32 getTicks();
+ uint32 getWinTicks();
+ uint32 getDosTicks(bool updateFl);
+ void waitForRefresh(void);
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:
+protected:
+ HugoEngine *_vm;
+
enum seqTextSchedule {
kSsNoBackground = 0,
kSsBadSaveGame = 1
};
- HugoEngine &_vm;
+ 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 _events[kMaxEvents]; // Statically declare event structures
- 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) */
+ virtual const char *getCypher() = 0;
+ virtual void delQueue(event_t *curEvent) = 0;
+ virtual event_t *doAction(event_t *curEvent) = 0;
event_t *getQueue();
- void delQueue(event_t *curEvent);
- event_t *doAction(event_t *curEvent);
-
- virtual const char *getCypher() = 0;
};
-class Scheduler_v1 : public Scheduler {
+class Scheduler_v1d : public Scheduler {
public:
- Scheduler_v1(HugoEngine &vm);
- ~Scheduler_v1();
+ Scheduler_v1d(HugoEngine *vm);
+ ~Scheduler_v1d();
+
+ virtual const char *getCypher();
+ virtual void insertAction(act *action);
+ virtual void restoreEvents(Common::SeekableReadStream *f);
+ virtual void saveEvents(Common::WriteStream *f);
+ virtual void runScheduler();
+protected:
+ virtual void delQueue(event_t *curEvent);
+ virtual event_t *doAction(event_t *curEvent);
+};
- const char *getCypher();
+class Scheduler_v2d : public Scheduler_v1d {
+public:
+ Scheduler_v2d(HugoEngine *vm);
+ virtual ~Scheduler_v2d();
+
+ virtual void insertAction(act *action);
+protected:
+ virtual void delQueue(event_t *curEvent);
+ virtual event_t *doAction(event_t *curEvent);
};
-class Scheduler_v2 : public Scheduler {
+class Scheduler_v3d : public Scheduler_v2d {
public:
- Scheduler_v2(HugoEngine &vm);
- ~Scheduler_v2();
+ Scheduler_v3d(HugoEngine *vm);
+ ~Scheduler_v3d();
const char *getCypher();
+protected:
+ virtual event_t *doAction(event_t *curEvent);
+
};
+class Scheduler_v1w : public Scheduler_v3d {
+public:
+ Scheduler_v1w(HugoEngine *vm);
+ ~Scheduler_v1w();
+
+ virtual event_t *doAction(event_t *curEvent);
+ void insertAction(act *action);
+ void restoreEvents(Common::SeekableReadStream *f);
+ void runScheduler();
+ void saveEvents(Common::WriteStream *f);
+};
} // 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..09bfc2294b
--- /dev/null
+++ b/engines/hugo/schedule_v1d.cpp
@@ -0,0 +1,406 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+#include "hugo/object.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";
+}
+
+// Delete an event structure (i.e. return it to the free list)
+// Note that event is assumed at head of queue (i.e. earliest). To delete
+// an event from the middle of the queue, merely overwrite its action type
+// to be ANULL
+void Scheduler_v1d::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
+
+ 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_v1d::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;
+
+ curEvent->localActionFl = true; // Rest are for current screen only
+
+ curEvent->time = action->a0.timer + getDosTicks(false); // 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;
+ }
+ }
+}
+
+event_t *Scheduler_v1d::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->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+ if (action->a3.encodedFl)
+ decodeString(response);
+
+ warning("STUB: doAction(act3), expecting answer %s", _vm->_file->fetchString(action->a3.responsePtr[0]));
+
+ // TODO: The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block
+#if 0
+ if (strstr (response, action->a3.response))
+ 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 velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_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->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_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
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_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->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_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->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_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
+ if (wrkEvent->action->a20.actType == action->a20.actTypeDel)
+ wrkEvent->action->a20.actType = ANULL;
+ wrkEvent = wrkEvent->nextEvent;
+ }
+ 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->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_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->_object->_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->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ insertActionList(action->a29.actPassIndex);
+ else
+ insertActionList(action->a29.actFailIndex);
+ 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
+ }
+}
+
+// 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_v1d::saveEvents(Common::WriteStream *f) {
+ debugC(1, kDebugSchedule, "saveEvents()");
+
+ uint32 curTime = getDosTicks(false);
+ 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_v1d::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 = getDosTicks(false);
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While mature events found
+ wrkEvent->time = wrkEvent->time - saveTime + curTime;
+ wrkEvent = wrkEvent->nextEvent;
+ }
+}
+
+// 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_v1d::runScheduler() {
+ debugC(6, kDebugSchedule, "runScheduler");
+
+ uint32 ticker; // The time now, in ticks
+ event_t *curEvent; // Event ptr
+
+ ticker = getDosTicks(false);
+
+ curEvent = _headEvent; // The earliest event
+ while (curEvent && curEvent->time <= ticker) // While mature events found
+ curEvent = doAction(curEvent); // Perform the action (returns next_p)
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v1w.cpp b/engines/hugo/schedule_v1w.cpp
new file mode 100644
index 0000000000..34eff0447f
--- /dev/null
+++ b/engines/hugo/schedule_v1w.cpp
@@ -0,0 +1,481 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.h"
+
+namespace Hugo {
+
+Scheduler_v1w::Scheduler_v1w(HugoEngine *vm) : Scheduler_v3d(vm) {
+}
+
+Scheduler_v1w::~Scheduler_v1w() {
+}
+
+event_t *Scheduler_v1w::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->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+
+ warning("STUB: doAction(act3), expecting answer %s", _vm->_file->fetchString(action->a3.responsePtr[0]));
+
+ // 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->fetchString(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 velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_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->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_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
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_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->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_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->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_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->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_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->_object->_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->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ 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->_object->_objects[action->a32.objNumb].priority = action->a32.priority;
+ break;
+ case INIT_SCREEN:
+ _vm->_object->_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->_object->_objects[action->a38.lipsObjNumb].x = _vm->_object->_objects[action->a38.objNumb].x + action->a38.dxLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].y = _vm->_object->_objects[action->a38.objNumb].y + action->a38.dyLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ _vm->_object->_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->_object->_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->_object->_objects[action->a47.objNumb].viewx = action->a47.viewx;
+ _vm->_object->_objects[action->a47.objNumb].viewy = action->a47.viewy;
+ _vm->_object->_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->_object->_objects[action->a48.objNumb].currImagePtr = _vm->_object->_objects[action->a48.objNumb].seqList[action->a48.seqIndex].seqPtr;
+ for (dx = 0; dx < action->a48.frameIndex; dx++)
+ _vm->_object->_objects[action->a48.objNumb].currImagePtr = _vm->_object->_objects[action->a48.objNumb].currImagePtr->nextSeqPtr;
+ 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
+ }
+}
+
+// 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_v1w::saveEvents(Common::WriteStream *f) {
+ debugC(1, kDebugSchedule, "saveEvents()");
+
+ uint32 curTime = getWinTicks();
+ 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_v1w::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 = getWinTicks();
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While mature events found
+ wrkEvent->time = wrkEvent->time - saveTime + curTime;
+ wrkEvent = wrkEvent->nextEvent;
+ }
+}
+
+void Scheduler_v1w::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 + getWinTicks(); // 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;
+ }
+ }
+}
+
+// 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_v1w::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()
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v2d.cpp b/engines/hugo/schedule_v2d.cpp
new file mode 100644
index 0000000000..2735bc4cf4
--- /dev/null
+++ b/engines/hugo/schedule_v2d.cpp
@@ -0,0 +1,385 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.h"
+
+namespace Hugo {
+
+Scheduler_v2d::Scheduler_v2d(HugoEngine *vm) : Scheduler_v1d(vm) {
+}
+
+Scheduler_v2d::~Scheduler_v2d() {
+}
+
+// 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, delQueue
+// was modified to allow deletes anywhere in the list, and the DEL_EVENT
+// action was modified to perform the actual delete.
+void Scheduler_v2d::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_v2d::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 + getDosTicks(false); // 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;
+ }
+ }
+}
+
+// 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
+event_t *Scheduler_v2d::doAction(event_t *curEvent) {
+ 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->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+
+ warning("STUB: doAction(act3), expecting answer %s", _vm->_file->fetchString(action->a3.responsePtr[0]));
+
+ // 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->fetchString(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 velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_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->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_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
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_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->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_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->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_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->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_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->_object->_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->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ 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->_object->_objects[action->a32.objNumb].priority = action->a32.priority;
+ break;
+ case INIT_SCREEN:
+ _vm->_object->_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->_object->_objects[action->a38.lipsObjNumb].x = _vm->_object->_objects[action->a38.objNumb].x + action->a38.dxLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].y = _vm->_object->_objects[action->a38.objNumb].y + action->a38.dyLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ _vm->_object->_objects[action->a38.lipsObjNumb].cycling = CYCLE_FORWARD;
+ 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
+ }
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v3d.cpp b/engines/hugo/schedule_v3d.cpp
new file mode 100644
index 0000000000..cc449ba8c4
--- /dev/null
+++ b/engines/hugo/schedule_v3d.cpp
@@ -0,0 +1,326 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+#include "hugo/object.h"
+
+namespace Hugo {
+
+Scheduler_v3d::Scheduler_v3d(HugoEngine *vm) : Scheduler_v2d(vm) {
+}
+
+Scheduler_v3d::~Scheduler_v3d() {
+}
+
+const char *Scheduler_v3d::getCypher() {
+ return "Copyright 1992, Gray Design Associates";
+}
+
+// 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
+event_t *Scheduler_v3d::doAction(event_t *curEvent) {
+ 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->_object->_objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm->_object->_objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm->_object->_objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm->_object->_objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+ response = Utils::Box(BOX_PROMPT, "%s", _vm->_file->fetchString(action->a3.promptIndex));
+
+ warning("STUB: doAction(act3), expecting answer %s", _vm->_file->fetchString(action->a3.responsePtr[0]));
+
+ // 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->fetchString(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 velocity
+ _vm->_object->setVelocity(action->a5.objNumb, action->a5.vx, action->a5.vy);
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm->_object->setCarry(action->a6.objNumb, action->a6.carriedFl); // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm->_object->_objects[action->a7.objNumb].x = _vm->_hero->x - 1;
+ _vm->_object->_objects[action->a7.objNumb].y = _vm->_hero->y + _vm->_hero->currImagePtr->y2 - 1;
+ _vm->_object->_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->_object->_objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm->_object->setPath(action->a10.objNumb, (path_t) action->a10.newPathType, action->a10.vxPath, action->a10.vyPath);
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm->_object->_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
+ _vm->_object->swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm->_object->_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->_object->_objects[action->a15.obj1];
+ obj2 = &_vm->_object->_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->_object->_objects[action->a16.objNumb].currImagePtr = _vm->_object->_objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm->_object->_objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm->_object->_objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm->_object->_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->_object->_objects[action->a22.objNumb].x = _vm->_hero->x;
+ _vm->_object->_objects[action->a22.objNumb].y = _vm->_hero->y;
+ _vm->_object->_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->_object->_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->_object->_objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm->adjustScore(-_vm->_object->_objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm->_object->isCarried(action->a29.objNumb))
+ 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->_object->_objects[action->a32.objNumb].priority = action->a32.priority;
+ break;
+ case INIT_SCREEN:
+ _vm->_object->_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->_object->_objects[action->a38.lipsObjNumb].x = _vm->_object->_objects[action->a38.objNumb].x + action->a38.dxLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].y = _vm->_object->_objects[action->a38.objNumb].y + action->a38.dyLips;
+ _vm->_object->_objects[action->a38.lipsObjNumb].screenIndex = *_vm->_screen_p; // Don't forget screen!
+ _vm->_object->_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 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
+ }
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp
index 7b6ae2ec24..b8a5a3297e 100644
--- a/engines/hugo/sound.cpp
+++ b/engines/hugo/sound.cpp
@@ -184,11 +184,11 @@ int MidiPlayer::open() {
void MidiPlayer::close() {
stop();
_mutex.lock();
- _driver->setTimerCallback(NULL, NULL);
+ _driver->setTimerCallback(0, 0);
_driver->close();
delete _driver;
_driver = 0;
- _parser->setMidiDriver(NULL);
+ _parser->setMidiDriver(0);
delete _parser;
_mutex.unlock();
}
@@ -239,7 +239,7 @@ void MidiPlayer::timerCallback(void *p) {
player->updateTimer();
}
-SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
+SoundHandler::SoundHandler(HugoEngine *vm) : _vm(vm) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
MidiDriver *driver = MidiDriver::createMidi(dev);
@@ -248,13 +248,13 @@ SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
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();
+ _vm->_mixer->stopAll();
}
void SoundHandler::stopMusic() {
@@ -265,7 +265,7 @@ void SoundHandler::stopMusic() {
void SoundHandler::toggleMusic() {
// Turn music on and off
_config.musicFl = !_config.musicFl;
-
+
_midiPlayer->pause(_config.musicFl);
}
@@ -285,8 +285,8 @@ void SoundHandler::playMusic(int16 tune) {
uint16 size; // Size of sequence data
if (_config.musicFl) {
- _vm.getGameStatus().song = tune;
- seqPtr = _vm.file().getSound(tune, &size);
+ _vm->getGameStatus().song = tune;
+ seqPtr = _vm->_file->getSound(tune, &size);
playMIDI(seqPtr, size);
}
}
@@ -302,7 +302,7 @@ void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
static byte curPriority = 0; // Priority of currently playing sound
//
/* Sound disabled */
- if (!_config.soundFl || !_vm._mixer->isReady())
+ if (!_config.soundFl || !_vm->_mixer->isReady())
return;
//
// // See if last wave still playing - if so, check priority
@@ -314,11 +314,11 @@ void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
curPriority = priority;
//
/* Get sound data */
- if ((sound_p = _vm.file().getSound(sound, &size)) == NULL)
+ 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);
+ _vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
}
diff --git a/engines/hugo/sound.h b/engines/hugo/sound.h
index 53a5912a92..ff38a7f70a 100644
--- a/engines/hugo/sound.h
+++ b/engines/hugo/sound.h
@@ -38,10 +38,10 @@
namespace Hugo {
class MidiPlayer;
-
+
class SoundHandler {
public:
- SoundHandler(HugoEngine &vm);
+ SoundHandler(HugoEngine *vm);
void toggleMusic();
void toggleSound();
@@ -51,7 +51,7 @@ public:
void initSound();
private:
- HugoEngine &_vm;
+ HugoEngine *_vm;
Audio::SoundHandle _soundHandle;
MidiPlayer *_midiPlayer;
diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp
index 8c63bedc04..2def4a4dba 100644
--- a/engines/hugo/util.cpp
+++ b/engines/hugo/util.cpp
@@ -41,95 +41,108 @@
namespace Hugo {
int Utils::firstBit(byte data) {
- /* Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found */
- int i;
-
+// Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found
if (!data)
- return(8);
+ return 8;
+ int i;
for (i = 0; i < 8; i++) {
if ((data << i) & 0x80)
break;
}
- return(i);
+ return i;
}
int Utils::lastBit(byte data) {
- /* Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found */
- int i;
+// Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found
if (!data)
- return(8);
+ return 8;
+ int i;
for (i = 7; i >= 0; i--) {
if ((data << i) & 0x80)
break;
}
- return(i);
+ return i;
}
void Utils::reverseByte(byte *data) {
- /* Reverse the bit order in supplied byte */
+// 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)
+ 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
- va_list marker;
- if (!s) return(NULL); // NULL strings catered for
+ if (!s)
+ return 0; // NULL strings catered for
if (s[0] == '\0')
- return(NULL);
+ return 0;
if (strlen(s) > MAX_STRLEN - 100) { // Test length
- Warn(false, "String too big:\n%s", s);
- return(NULL);
+ 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);
- //Warn(false, "BOX: %s", buffer);
- int boxTime = strlen(buffer) * 30;
- GUI::TimedMessageDialog dialog(buffer, MAX(1500, boxTime));
- dialog.runModal();
+ 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(bool technote, const char *format, ...) {
- /* Warning handler. Print supplied message and continue */
- /* Arguments are same as printf */
- /* technote TRUE if we are to refer user to technote file */
+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);
-//// if (technote)
-//// strcat (buffer, sTech);
- //MessageBeep(MB_ICONEXCLAMATION);
- //MessageBox(hwnd, buffer, "HugoWin Warning", MB_OK | MB_ICONEXCLAMATION);
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 */
- va_list marker;
+// 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
@@ -158,6 +171,7 @@ void Utils::Error(int error_type, const char *format, ...) {
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);
@@ -175,4 +189,17 @@ void Utils::gameOverMsg(void) {
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
index f944baa324..69428fb3bb 100644
--- a/engines/hugo/util.h
+++ b/engines/hugo/util.h
@@ -51,11 +51,14 @@ enum seqTextUtil {
namespace Utils {
int firstBit(byte data);
int lastBit(byte data);
+
+void gameOverMsg();
void reverseByte(byte *data);
-void Warn(bool technote, const char *format, ...) GCC_PRINTF(2, 3);
void Error(int code, const char *format, ...) GCC_PRINTF(2, 3);
-void gameOverMsg();
+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
diff --git a/engines/kyra/animator_v2.cpp b/engines/kyra/animator_v2.cpp
index 6c4fafa674..b06dffd36f 100644
--- a/engines/kyra/animator_v2.cpp
+++ b/engines/kyra/animator_v2.cpp
@@ -141,7 +141,7 @@ void KyraEngine_v2::flagAnimObjsSpecialRefresh() {
}
void KyraEngine_v2::addItemToAnimList(int item) {
- assert(item < _itemListSize);
+ assert(item >= 0 && item < _itemListSize);
restorePage3();
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 2e1d5afc17..5c97df8895 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 }
},
@@ -1130,6 +1146,23 @@ const KYRAGameDescription adGameDescs[] = {
"lol",
"Extracted",
{
+ { "GENERAL.PAK", 0, "d119e3b57f8e5edcbb90980ca6f4215a", -1 },
+ { "CHAPTER7.PAK", 0, "71a3d3cb1554294646a389e5c345cf28", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
{ "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
{ "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
{ 0, 0, 0, 0 }
diff --git a/engines/kyra/gui_hof.cpp b/engines/kyra/gui_hof.cpp
index 621b3199c5..56971e563c 100644
--- a/engines/kyra/gui_hof.cpp
+++ b/engines/kyra/gui_hof.cpp
@@ -122,9 +122,9 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
int inventorySlot = button->index - 6;
- uint16 item = _mainCharacter.inventory[inventorySlot];
- if (_itemInHand == -1) {
- if (item == 0xFFFF)
+ Item item = _mainCharacter.inventory[inventorySlot];
+ if (_itemInHand == kItemNone) {
+ if (item == kItemNone)
return 0;
_screen->hideMouse();
clearInventorySlot(inventorySlot, 0);
@@ -134,9 +134,9 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
updateCommandLineEx(item+54, string, 0xD6);
_itemInHand = (int16)item;
_screen->showMouse();
- _mainCharacter.inventory[inventorySlot] = 0xFFFF;
+ _mainCharacter.inventory[inventorySlot] = kItemNone;
} else {
- if (_mainCharacter.inventory[inventorySlot] != 0xFFFF) {
+ if (_mainCharacter.inventory[inventorySlot] != kItemNone) {
if (checkInventoryItemExchange(_itemInHand, inventorySlot))
return 0;
@@ -160,7 +160,7 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
updateCommandLineEx(_itemInHand+54, string, 0xD6);
_screen->showMouse();
_mainCharacter.inventory[inventorySlot] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
@@ -168,15 +168,15 @@ int KyraEngine_HoF::buttonInventory(Button *button) {
}
int KyraEngine_HoF::scrollInventory(Button *button) {
- uint16 *src = _mainCharacter.inventory;
- uint16 *dst = &_mainCharacter.inventory[10];
- uint16 temp[5];
-
- memcpy(temp, src, sizeof(uint16)*5);
- memcpy(src, src+5, sizeof(uint16)*5);
- memcpy(src+5, dst, sizeof(uint16)*5);
- memcpy(dst, dst+5, sizeof(uint16)*5);
- memcpy(dst+5, temp, sizeof(uint16)*5);
+ Item *src = _mainCharacter.inventory;
+ Item *dst = &_mainCharacter.inventory[10];
+ Item temp[5];
+
+ memcpy(temp, src, sizeof(Item)*5);
+ memcpy(src, src+5, sizeof(Item)*5);
+ memcpy(src+5, dst, sizeof(Item)*5);
+ memcpy(dst, dst+5, sizeof(Item)*5);
+ memcpy(dst+5, temp, sizeof(Item)*5);
_screen->hideMouse();
_screen->copyRegion(0x46, 0x90, 0x46, 0x90, 0x71, 0x2E, 0, 2);
_screen->showMouse();
@@ -185,7 +185,7 @@ int KyraEngine_HoF::scrollInventory(Button *button) {
return 0;
}
-int KyraEngine_HoF::getInventoryItemSlot(uint16 item) {
+int KyraEngine_HoF::getInventoryItemSlot(Item item) {
for (int i = 0; i < 20; ++i) {
if (_mainCharacter.inventory[i] == item)
return i;
@@ -195,14 +195,14 @@ int KyraEngine_HoF::getInventoryItemSlot(uint16 item) {
int KyraEngine_HoF::findFreeVisibleInventorySlot() {
for (int i = 0; i < 10; ++i) {
- if (_mainCharacter.inventory[i] == 0xFFFF)
+ if (_mainCharacter.inventory[i] == kItemNone)
return i;
}
return -1;
}
void KyraEngine_HoF::removeSlotFromInventory(int slot) {
- _mainCharacter.inventory[slot] = 0xFFFF;
+ _mainCharacter.inventory[slot] = kItemNone;
if (slot < 10) {
_screen->hideMouse();
clearInventorySlot(slot, 0);
@@ -210,21 +210,21 @@ void KyraEngine_HoF::removeSlotFromInventory(int slot) {
}
}
-bool KyraEngine_HoF::checkInventoryItemExchange(uint16 handItem, int slot) {
+bool KyraEngine_HoF::checkInventoryItemExchange(Item handItem, int slot) {
bool removeItem = false;
- uint16 newItem = 0xFFFF;
+ Item newItem = kItemNone;
- uint16 invItem = _mainCharacter.inventory[slot];
+ Item invItem = _mainCharacter.inventory[slot];
for (const uint16 *table = _itemMagicTable; *table != 0xFFFF; table += 4) {
- if (table[0] != handItem || table[1] != invItem)
+ if (table[0] != handItem || table[1] != (uint16)invItem)
continue;
if (table[3] == 0xFFFF)
continue;
removeItem = (table[3] == 1);
- newItem = table[2];
+ newItem = (Item)table[2];
snd_playSoundEffect(0x68);
_mainCharacter.inventory[slot] = newItem;
@@ -246,7 +246,7 @@ bool KyraEngine_HoF::checkInventoryItemExchange(uint16 handItem, int slot) {
return false;
}
-void KyraEngine_HoF::drawInventoryShape(int page, uint16 item, int slot) {
+void KyraEngine_HoF::drawInventoryShape(int page, Item item, int slot) {
_screen->drawShape(page, getShapePtr(item+64), _inventoryX[slot], _inventoryY[slot], 0, 0);
_screen->updateScreen();
}
@@ -260,11 +260,11 @@ void KyraEngine_HoF::redrawInventory(int page) {
int pageBackUp = _screen->_curPage;
_screen->_curPage = page;
- const uint16 *inventory = _mainCharacter.inventory;
+ const Item *inventory = _mainCharacter.inventory;
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
clearInventorySlot(i, page);
- if (inventory[i] != 0xFFFF) {
+ if (inventory[i] != kItemNone) {
_screen->drawShape(page, getShapePtr(inventory[i]+64), _inventoryX[i], _inventoryY[i], 0, 0);
drawInventoryShape(page, inventory[i], i);
}
@@ -734,7 +734,7 @@ int GUI_HoF::optionsButton(Button *button) {
if (_loadedSave) {
if (_restartGame)
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
} else {
restorePage1(_vm->_screenBuffer);
restorePalette();
@@ -820,7 +820,7 @@ void GUI_HoF::resetState(int item) {
_vm->setNextIdleAnimTimer();
_isDeathMenu = false;
if (!_loadedSave) {
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
_vm->setHandItem(item);
} else {
_vm->setHandItem(_vm->_itemInHand);
@@ -998,7 +998,7 @@ int GUI_HoF::gameOptionsTalkie(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(999, "Autosave", &thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
thumb.free();
_vm->_lastAutosave = _vm->_system->getMillis();
diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp
index 9a1d750391..b7952eb81e 100644
--- a/engines/kyra/gui_lok.cpp
+++ b/engines/kyra/gui_lok.cpp
@@ -32,6 +32,7 @@
#include "kyra/gui_lok.h"
#include "kyra/timer.h"
#include "kyra/util.h"
+#include "kyra/item.h"
#include "common/config-manager.h"
#include "common/savefile.h"
@@ -49,9 +50,9 @@ void KyraEngine_LoK::initMainButtonList() {
int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
int itemOffset = caller->index - 2;
- uint8 inventoryItem = _currentCharacter->inventoryItems[itemOffset];
- if (_itemInHand == -1) {
- if (inventoryItem == 0xFF) {
+ Item inventoryItem = (int8)_currentCharacter->inventoryItems[itemOffset];
+ if (_itemInHand == kItemNone) {
+ if (inventoryItem == kItemNone) {
snd_playSoundEffect(0x36);
return 0;
} else {
@@ -62,10 +63,10 @@ int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
updateSentenceCommand(_itemList[getItemListIndex(inventoryItem)], _takenList[0], 179);
_itemInHand = inventoryItem;
_screen->showMouse();
- _currentCharacter->inventoryItems[itemOffset] = 0xFF;
+ _currentCharacter->inventoryItems[itemOffset] = kItemNone;
}
} else {
- if (inventoryItem != 0xFF) {
+ if (inventoryItem != kItemNone) {
snd_playSoundEffect(0x35);
_screen->hideMouse();
_screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12);
@@ -87,7 +88,7 @@ int KyraEngine_LoK::buttonInventoryCallback(Button *caller) {
updateSentenceCommand(_itemList[getItemListIndex(_itemInHand)], _placedList[0], 179);
_screen->showMouse();
_currentCharacter->inventoryItems[itemOffset] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
_screen->updateScreen();
@@ -104,7 +105,7 @@ int KyraEngine_LoK::buttonAmuletCallback(Button *caller) {
}
if (!queryGameFlag(0x2D))
return 1;
- if (_itemInHand != -1) {
+ if (_itemInHand != kItemNone) {
assert(_putDownFirst);
characterSays(2000, _putDownFirst[0], 0, -2);
return 1;
@@ -777,7 +778,7 @@ int GUI_LoK::saveGame(Button *button) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(_vm->_gameToLoad, _savegameName, &thumb);
+ _vm->saveGameStateIntern(_vm->_gameToLoad, _savegameName, &thumb);
thumb.free();
}
}
diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp
index 2c86073892..07fbf1664d 100644
--- a/engines/kyra/gui_lol.cpp
+++ b/engines/kyra/gui_lol.cpp
@@ -2906,7 +2906,7 @@ int GUI_LoL::clickedSavenameMenu(Button *button) {
int slot = _menuResult == -2 ? getNextSavegameSlot() : _menuResult - 1;
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(slot, _saveDescription, &thumb);
+ _vm->saveGameStateIntern(slot, _saveDescription, &thumb);
thumb.free();
_displayMenu = false;
diff --git a/engines/kyra/gui_mr.cpp b/engines/kyra/gui_mr.cpp
index bcc74f5a07..a04ec49324 100644
--- a/engines/kyra/gui_mr.cpp
+++ b/engines/kyra/gui_mr.cpp
@@ -130,7 +130,7 @@ void KyraEngine_MR::showMessageFromCCode(int string, uint8 c0, int) {
showMessage((const char *)getTableEntry(_cCodeFile, string), c0, 0xF0);
}
-void KyraEngine_MR::updateItemCommand(int item, int str, uint8 c0) {
+void KyraEngine_MR::updateItemCommand(Item item, int str, uint8 c0) {
char buffer[100];
char *src = (char *)getTableEntry(_itemFile, item);
@@ -449,7 +449,7 @@ void KyraEngine_MR::redrawInventory(int page) {
for (int i = 0; i < 10; ++i) {
clearInventorySlot(i, page);
- if (_mainCharacter.inventory[i] != 0xFFFF) {
+ if (_mainCharacter.inventory[i] != kItemNone) {
_screen->drawShape(page, getShapePtr(_mainCharacter.inventory[i]+248), _inventoryX[i], _inventoryY[i] + yOffset, 0, 0);
drawInventorySlot(page, _mainCharacter.inventory[i], i);
}
@@ -472,7 +472,7 @@ void KyraEngine_MR::clearInventorySlot(int slot, int page) {
_screen->drawShape(page, getShapePtr(slot+422), _inventoryX[slot], _inventoryY[slot] + yOffset, 0, 0);
}
-void KyraEngine_MR::drawInventorySlot(int page, int item, int slot) {
+void KyraEngine_MR::drawInventorySlot(int page, Item item, int slot) {
int yOffset = 0;
if (page == 30) {
page = 2;
@@ -488,9 +488,9 @@ int KyraEngine_MR::buttonInventory(Button *button) {
return 0;
const int slot = button->index - 5;
- const int16 slotItem = (int16)_mainCharacter.inventory[slot];
- if (_itemInHand == -1) {
- if (slotItem == -1)
+ const Item slotItem = _mainCharacter.inventory[slot];
+ if (_itemInHand == kItemNone) {
+ if (slotItem == kItemNone)
return 0;
_screen->hideMouse();
@@ -499,7 +499,7 @@ int KyraEngine_MR::buttonInventory(Button *button) {
setMouseCursor(slotItem);
updateItemCommand(slotItem, (_lang == 1) ? getItemCommandStringPickUp(slotItem) : 0, 0xFF);
_itemInHand = slotItem;
- _mainCharacter.inventory[slot] = 0xFFFF;
+ _mainCharacter.inventory[slot] = kItemNone;
_screen->showMouse();
} else if (_itemInHand == 27) {
if (_chatText)
@@ -528,7 +528,7 @@ int KyraEngine_MR::buttonInventory(Button *button) {
updateItemCommand(_itemInHand, (_lang == 1) ? getItemCommandStringInv(_itemInHand) : 2, 0xFF);
_screen->showMouse();
_mainCharacter.inventory[slot] = _itemInHand;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
}
}
@@ -635,7 +635,7 @@ int KyraEngine_MR::buttonJesterStaff(Button *button) {
updateItemCommand(27, 2, 0xFF);
setGameFlag(0x97);
_screen->showMouse();
- } else if (_itemInHand == -1) {
+ } else if (_itemInHand == kItemNone) {
if (queryGameFlag(0x97)) {
_screen->hideMouse();
snd_playSoundEffect(0x0B, 0xC8);
@@ -1141,7 +1141,7 @@ void GUI_MR::resetState(int item) {
_vm->setNextIdleAnimTimer();
_isDeathMenu = false;
if (!_loadedSave) {
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
_vm->setHandItem(item);
} else {
_vm->setHandItem(_vm->_itemInHand);
@@ -1239,7 +1239,7 @@ int GUI_MR::optionsButton(Button *button) {
if (_loadedSave) {
if (_restartGame)
- _vm->_itemInHand = -1;
+ _vm->_itemInHand = kItemNone;
} else {
restorePage1(_vm->_screenBuffer);
}
@@ -1380,7 +1380,7 @@ int GUI_MR::gameOptions(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
- _vm->saveGameState(999, "Autosave", &thumb);
+ _vm->saveGameStateIntern(999, "Autosave", &thumb);
thumb.free();
_vm->_lastAutosave = _vm->_system->getMillis();
diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp
index fe4b54d09b..2247a0ca2e 100644
--- a/engines/kyra/gui_v2.cpp
+++ b/engines/kyra/gui_v2.cpp
@@ -625,7 +625,7 @@ int GUI_v2::saveMenu(Button *caller) {
Graphics::Surface thumb;
createScreenThumbnail(thumb);
Util::convertDOSToISO(_saveDescription);
- _vm->saveGameState(_saveSlot, _saveDescription, &thumb);
+ _vm->saveGameStateIntern(_saveSlot, _saveDescription, &thumb);
thumb.free();
_displayMenu = false;
diff --git a/engines/hugo/engine.h b/engines/kyra/item.h
index 0d14d244b5..2088f4bd8b 100644
--- a/engines/hugo/engine.h
+++ b/engines/kyra/item.h
@@ -23,22 +23,23 @@
*
*/
-/*
- * This code is based on original Hugo 1-3 Trilogy source code
- *
- * Copyright (c) 1989-1995 David P. Gray
- *
- */
+#ifndef KYRA_ITEM_H
+#define KYRA_ITEM_H
-#ifndef HUGO_ENGINE_H
-#define HUGO_ENGINE_H
-namespace Hugo {
+#include "common/scummsys.h"
-enum seqTextEngine {
- // Strings used by the engine
- kEsAdvertise = 0
+namespace Kyra {
+
+typedef int16 Item;
+
+enum {
+ /**
+ * Constant for invalid item.
+ */
+ kItemNone = -1
};
-} // End of namespace Hugo
+} // End of namespace Kyra
+
+#endif
-#endif // HUGO_ENGINE_H
diff --git a/engines/kyra/items_hof.cpp b/engines/kyra/items_hof.cpp
index 876db58716..6a78a77c23 100644
--- a/engines/kyra/items_hof.cpp
+++ b/engines/kyra/items_hof.cpp
@@ -31,9 +31,9 @@ int KyraEngine_HoF::checkItemCollision(int x, int y) {
int itemPos = -1, yPos = -1;
for (int i = 0; i < 30; ++i) {
- const Item &curItem = _itemList[i];
+ const ItemDefinition &curItem = _itemList[i];
- if (curItem.id == 0xFFFF || curItem.sceneId != _mainCharacter.sceneId)
+ if (curItem.id == kItemNone || curItem.sceneId != _mainCharacter.sceneId)
continue;
int itemX1 = curItem.x - 8 - 3;
@@ -79,7 +79,7 @@ void KyraEngine_HoF::updateWaterFlasks() {
}
}
-bool KyraEngine_HoF::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
+bool KyraEngine_HoF::dropItem(int unk1, Item item, int x, int y, int unk2) {
if (_mouseState <= -1)
return false;
@@ -93,7 +93,7 @@ bool KyraEngine_HoF::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
return success;
}
-bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2) {
+bool KyraEngine_HoF::processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2) {
int itemPos = checkItemCollision(x, y);
if (unk1)
@@ -108,7 +108,7 @@ bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y,
if (unk1 != 3) {
for (int i = 0; i < 30; ++i) {
- if (_itemList[i].id == 0xFFFF) {
+ if (_itemList[i].id == kItemNone) {
freeItemSlot = i;
break;
}
@@ -200,7 +200,7 @@ bool KyraEngine_HoF::processItemDrop(uint16 sceneId, uint16 item, int x, int y,
return true;
}
-void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item) {
+void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item) {
uint8 *itemShape = getShapePtr(item + 64);
if (startX == dstX && startY == dstY) {
@@ -335,7 +335,7 @@ bool KyraEngine_HoF::pickUpItem(int x, int y) {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
int itemId = _itemList[itemPos].id;
- _itemList[itemPos].id = 0xFFFF;
+ _itemList[itemPos].id = kItemNone;
snd_playSoundEffect(0x0b);
setMouseCursor(itemId);
int str2 = 7;
@@ -368,8 +368,8 @@ bool KyraEngine_HoF::isDropable(int x, int y) {
return true;
}
-int KyraEngine_HoF::getItemCommandStringDrop(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringDrop(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int dropStringIds[] = {
@@ -380,8 +380,8 @@ int KyraEngine_HoF::getItemCommandStringDrop(uint16 item) {
return dropStringIds[stringId];
}
-int KyraEngine_HoF::getItemCommandStringPickUp(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringPickUp(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int pickUpStringIds[] = {
@@ -392,8 +392,8 @@ int KyraEngine_HoF::getItemCommandStringPickUp(uint16 item) {
return pickUpStringIds[stringId];
}
-int KyraEngine_HoF::getItemCommandStringInv(uint16 item) {
- assert(item < _itemStringMapSize);
+int KyraEngine_HoF::getItemCommandStringInv(Item item) {
+ assert(item >= 0 && item < _itemStringMapSize);
int stringId = _itemStringMap[item];
static const int pickUpStringIds[] = {
@@ -404,8 +404,8 @@ int KyraEngine_HoF::getItemCommandStringInv(uint16 item) {
return pickUpStringIds[stringId];
}
-bool KyraEngine_HoF::itemIsFlask(int item) {
- for (int i = 0; _flaskTable[i] != -1; ++i) {
+bool KyraEngine_HoF::itemIsFlask(Item item) {
+ for (int i = 0; _flaskTable[i] != kItemNone; ++i) {
if (_flaskTable[i] == item)
return true;
}
@@ -413,12 +413,12 @@ bool KyraEngine_HoF::itemIsFlask(int item) {
return false;
}
-void KyraEngine_HoF::setMouseCursor(uint16 item) {
+void KyraEngine_HoF::setMouseCursor(Item item) {
int shape = 0;
int hotX = 1;
int hotY = 1;
- if (item != 0xFFFF) {
+ if (item != kItemNone) {
hotX = 8;
hotY = 15;
shape = item+64;
diff --git a/engines/kyra/items_lok.cpp b/engines/kyra/items_lok.cpp
index df46dfa4cd..322314e3ad 100644
--- a/engines/kyra/items_lok.cpp
+++ b/engines/kyra/items_lok.cpp
@@ -74,29 +74,31 @@ void KyraEngine_LoK::clearNoDropRects() {
byte KyraEngine_LoK::findFreeItemInScene(int scene) {
assert(scene < _roomTableSize);
Room *room = &_roomTable[scene];
+
for (int i = 0; i < 12; ++i) {
- if (room->itemsTable[i] == 0xFF)
+ if (room->itemsTable[i] == kItemNone)
return i;
}
+
return 0xFF;
}
byte KyraEngine_LoK::findItemAtPos(int x, int y) {
assert(_currentCharacter->sceneId < _roomTableSize);
- const uint8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
+ const int8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
const uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos;
const uint8 *yposOffset = _roomTable[_currentCharacter->sceneId].itemsYPos;
int highestYPos = -1;
- byte returnValue = 0xFF;
+ Item returnValue = kItemNone;
for (int i = 0; i < 12; ++i) {
- if (*itemsTable != 0xFF) {
+ if (*itemsTable != kItemNone) {
int xpos = *xposOffset - 11;
int xpos2 = *xposOffset + 10;
if (x > xpos && x < xpos2) {
- assert(*itemsTable < ARRAYSIZE(_itemTable));
- int itemHeight = _itemTable[*itemsTable].height;
+ assert(*itemsTable >= 0);
+ int itemHeight = _itemHtDat[*itemsTable];
int ypos = *yposOffset + 3;
int ypos2 = ypos - itemHeight - 3;
@@ -108,6 +110,7 @@ byte KyraEngine_LoK::findItemAtPos(int x, int y) {
}
}
}
+
++xposOffset;
++yposOffset;
++itemsTable;
@@ -170,7 +173,7 @@ void KyraEngine_LoK::placeItemInGenericMapScene(int item, int index) {
}
}
-void KyraEngine_LoK::setHandItem(uint16 item) {
+void KyraEngine_LoK::setHandItem(Item item) {
_screen->hideMouse();
setMouseItem(item);
_itemInHand = item;
@@ -180,20 +183,21 @@ void KyraEngine_LoK::setHandItem(uint16 item) {
void KyraEngine_LoK::removeHandItem() {
_screen->hideMouse();
_screen->setMouseCursor(1, 1, _shapes[0]);
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_screen->showMouse();
}
-void KyraEngine_LoK::setMouseItem(uint16 item) {
- if (item == 0xFFFF)
+void KyraEngine_LoK::setMouseItem(Item item) {
+ if (item == kItemNone)
_screen->setMouseCursor(1, 1, _shapes[6]);
else
_screen->setMouseCursor(8, 15, _shapes[216+item]);
}
void KyraEngine_LoK::wipeDownMouseItem(int xpos, int ypos) {
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
return;
+
xpos -= 8;
ypos -= 15;
_screen->hideMouse();
@@ -261,7 +265,7 @@ int KyraEngine_LoK::countItemsInScene(uint16 sceneId) {
int items = 0;
for (int i = 0; i < 12; ++i) {
- if (currentRoom->itemsTable[i] != 0xFF)
+ if (currentRoom->itemsTable[i] != kItemNone)
++items;
}
@@ -284,7 +288,7 @@ int KyraEngine_LoK::processItemDrop(uint16 sceneId, uint8 item, int x, int y, in
if (unk1 != 3) {
for (int i = 0; i < 12; ++i) {
- if (currentRoom->itemsTable[i] == 0xFF) {
+ if (currentRoom->itemsTable[i] == kItemNone) {
freeItem = i;
break;
}
@@ -301,13 +305,14 @@ int KyraEngine_LoK::processItemDrop(uint16 sceneId, uint8 item, int x, int y, in
return 1;
}
- int itemHeight = _itemTable[item].height;
+ int itemHeight = _itemHtDat[item];
_lastProcessedItemHeight = itemHeight;
- if (x == -1 && x == -1) {
+ if (x == -1)
x = _rnd.getRandomNumberRng(16, 304);
+
+ if (y == -1)
y = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 135);
- }
int xpos = x;
int ypos = y;
@@ -618,7 +623,7 @@ void KyraEngine_LoK::itemSpecialFX1(int x, int y, int item) {
void KyraEngine_LoK::itemSpecialFX2(int x, int y, int item) {
x -= 8;
y -= 15;
- int yAdd = (int8)(((16 - _itemTable[item].height) >> 1) & 0xFF);
+ int yAdd = (int8)(((16 - _itemHtDat[item]) >> 1) & 0xFF);
backUpItemRect0(x, y);
if (item >= 80 && item <= 89)
snd_playSoundEffect(55);
@@ -646,7 +651,8 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
int videoPageBackUp = _screen->_curPage;
_screen->_curPage = 0;
int x = 0, y = 0;
- if (itemPos == -1) {
+
+ if (itemPos == kItemNone) {
Common::Point mouse = getMousePos();
x = mouse.x - 12;
y = mouse.y - 18;
@@ -655,7 +661,7 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
y = _itemPosY[itemPos] - 3;
}
- if (_itemInHand == -1 && itemPos == -1)
+ if (_itemInHand == kItemNone && itemPos == -1)
return;
int tableIndex = 0, loopStart = 0, maxLoops = 0;
@@ -712,15 +718,17 @@ void KyraEngine_LoK::magicOutMouseItem(int animIndex, int itemPos) {
delayUntil(nextTime);
}
restoreItemRect1(x, y);
+
if (itemPos == -1) {
_screen->setMouseCursor(1, 1, _shapes[0]);
- _itemInHand = -1;
+ _itemInHand = kItemNone;
} else {
- _characterList[0].inventoryItems[itemPos] = 0xFF;
+ _characterList[0].inventoryItems[itemPos] = kItemNone;
_screen->hideMouse();
_screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, 0);
_screen->showMouse();
}
+
_screen->showMouse();
_screen->_curPage = videoPageBackUp;
}
@@ -883,7 +891,8 @@ void KyraEngine_LoK::redrawInventory(int page) {
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
_screen->fillRect(_itemPosX[i], _itemPosY[i], _itemPosX[i] + 15, _itemPosY[i] + 15, _flags.platform == Common::kPlatformAmiga ? 19 : 12, page);
- if (_currentCharacter->inventoryItems[i] != 0xFF) {
+
+ if (_currentCharacter->inventoryItems[i] != kItemNone) {
uint8 item = _currentCharacter->inventoryItems[i];
_screen->drawShape(page, _shapes[216+item], _itemPosX[i], _itemPosY[i], 0, 0);
}
@@ -913,12 +922,12 @@ void KyraEngine_LoK::restoreItemRect1(int xpos, int ypos) {
_screen->copyBlockToPage(_screen->_curPage, xpos, ypos, 4<<3, 32, _itemBkgBackUp[1]);
}
-int KyraEngine_LoK::getItemListIndex(uint16 item) {
+int KyraEngine_LoK::getItemListIndex(Item item) {
if (_flags.platform != Common::kPlatformAmiga)
return item;
// "Unknown item" is at 81.
- if (item == 0xFFFF || item == 0xFF)
+ if (item == kItemNone)
return 81;
// The first item names are mapped directly
else if (item <= 28)
diff --git a/engines/kyra/items_lol.cpp b/engines/kyra/items_lol.cpp
index 5b566d51db..e99f3bf701 100644
--- a/engines/kyra/items_lol.cpp
+++ b/engines/kyra/items_lol.cpp
@@ -110,10 +110,10 @@ void LoLEngine::takeCredits(int credits, int redraw) {
}
}
-int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
+Item LoLEngine::makeItem(int itemType, int curFrame, int flags) {
int cnt = 0;
int r = 0;
- int i = 1;
+ Item i = 1;
for (; i < 400; i++) {
if (_itemsInPlay[i].shpCurFrame_flg & 0x8000) {
@@ -130,7 +130,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
continue;
bool t = false;
- int ii = i;
+ Item ii = i;
while (ii && !t) {
t = testUnkItemFlags(ii);
if (t)
@@ -145,7 +145,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
}
}
- int slot = i;
+ Item slot = i;
if (cnt) {
slot = r;
if (testUnkItemFlags(r)) {
@@ -154,7 +154,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
deleteItem(r);
slot = r;
} else {
- int ii = _itemsInPlay[slot].nextAssignedObject;
+ uint16 ii = _itemsInPlay[slot].nextAssignedObject;
while (ii) {
if (testUnkItemFlags(ii)) {
_itemsInPlay[slot].nextAssignedObject = _itemsInPlay[ii].nextAssignedObject;
@@ -178,7 +178,7 @@ int LoLEngine::makeItem(int itemType, int curFrame, int flags) {
return slot;
}
-void LoLEngine::placeMoveLevelItem(int itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight) {
+void LoLEngine::placeMoveLevelItem(Item itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight) {
calcCoordinates(_itemsInPlay[itemIndex].x, _itemsInPlay[itemIndex].y, block, xOffs, yOffs);
if (_itemsInPlay[itemIndex].block)
@@ -194,7 +194,7 @@ void LoLEngine::placeMoveLevelItem(int itemIndex, int level, int block, int xOff
}
}
-bool LoLEngine::addItemToInventory(int itemIndex) {
+bool LoLEngine::addItemToInventory(Item itemIndex) {
int pos = 0;
int i = 0;
@@ -222,7 +222,7 @@ bool LoLEngine::addItemToInventory(int itemIndex) {
return true;
}
-bool LoLEngine::testUnkItemFlags(int itemIndex) {
+bool LoLEngine::testUnkItemFlags(Item itemIndex) {
if (!(_itemsInPlay[itemIndex].shpCurFrame_flg & 0x4000))
return false;
@@ -233,7 +233,7 @@ bool LoLEngine::testUnkItemFlags(int itemIndex) {
}
-void LoLEngine::deleteItem(int itemIndex) {
+void LoLEngine::deleteItem(Item itemIndex) {
memset(&_itemsInPlay[itemIndex], 0, sizeof(ItemInPlay));
_itemsInPlay[itemIndex].shpCurFrame_flg |= 0x8000;
}
@@ -245,7 +245,7 @@ ItemInPlay *LoLEngine::findObject(uint16 index) {
return &_itemsInPlay[index];
}
-void LoLEngine::runItemScript(int charNum, int item, int flags, int next, int reg4) {
+void LoLEngine::runItemScript(int charNum, Item item, int flags, int next, int reg4) {
EMCState scriptState;
memset(&scriptState, 0, sizeof(EMCState));
@@ -270,7 +270,7 @@ void LoLEngine::runItemScript(int charNum, int item, int flags, int next, int re
}
}
-void LoLEngine::setHandItem(uint16 itemIndex) {
+void LoLEngine::setHandItem(Item itemIndex) {
if (itemIndex && _itemProperties[_itemsInPlay[itemIndex].itemPropertyIndex].flags & 0x80) {
runItemScript(-1, itemIndex, 0x400, 0, 0);
if (_itemsInPlay[itemIndex].shpCurFrame_flg & 0x8000)
@@ -307,7 +307,7 @@ bool LoLEngine::itemEquipped(int charNum, uint16 itemType) {
return false;
}
-void LoLEngine::setItemPosition(int item, uint16 x, uint16 y, int flyingHeight, int b) {
+void LoLEngine::setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int b) {
if (!flyingHeight) {
x = (x & 0xffc0) | 0x40;
y = (y & 0xffc0) | 0x40;
@@ -334,7 +334,7 @@ void LoLEngine::setItemPosition(int item, uint16 x, uint16 y, int flyingHeight,
checkSceneUpdateNeed(block);
}
-void LoLEngine::removeLevelItem(int item, int block) {
+void LoLEngine::removeLevelItem(Item item, int block) {
removeAssignedObjectFromBlock(&_levelBlockProperties[block], item);
removeDrawObjectFromBlock(&_levelBlockProperties[block], item);
runLevelScriptCustom(block, 0x100, -1, item, 0, 0);
@@ -342,7 +342,7 @@ void LoLEngine::removeLevelItem(int item, int block) {
_itemsInPlay[item].level = 0;
}
-bool LoLEngine::launchObject(int objectType, int item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c) {
+bool LoLEngine::launchObject(int objectType, Item item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c) {
int sp = checkDrawObjectSpace(_partyPosX, _partyPosY, startX, startY);
FlyingObject *t = _flyingObjects;
int slot = -1;
diff --git a/engines/kyra/items_mr.cpp b/engines/kyra/items_mr.cpp
index 256753cc69..2bc268ace3 100644
--- a/engines/kyra/items_mr.cpp
+++ b/engines/kyra/items_mr.cpp
@@ -29,7 +29,7 @@
namespace Kyra {
void KyraEngine_MR::removeTrashItems() {
- for (int i = 0; _trashItemList[i] != 0xFF; ++i) {
+ for (int i = 0; _trashItemList[i] != kItemNone; ++i) {
for (int item = findItem(_trashItemList[i]); item != -1; item = findItem(_trashItemList[i])) {
if (_itemList[item].sceneId != _mainCharacter.sceneId)
resetItem(item);
@@ -41,7 +41,7 @@ void KyraEngine_MR::removeTrashItems() {
int KyraEngine_MR::findFreeInventorySlot() {
for (int i = 0; i < 10; ++i) {
- if (_mainCharacter.inventory[i] == 0xFFFF)
+ if (_mainCharacter.inventory[i] == kItemNone)
return i;
}
return -1;
@@ -52,7 +52,7 @@ int KyraEngine_MR::checkItemCollision(int x, int y) {
int maxItemY = -1;
for (int i = 0; i < 50; ++i) {
- if (_itemList[i].id == 0xFFFF || _itemList[i].sceneId != _mainCharacter.sceneId)
+ if (_itemList[i].id == kItemNone || _itemList[i].sceneId != _mainCharacter.sceneId)
continue;
const int x1 = _itemList[i].x - 11;
@@ -76,12 +76,12 @@ int KyraEngine_MR::checkItemCollision(int x, int y) {
return itemIndex;
}
-void KyraEngine_MR::setMouseCursor(uint16 item) {
+void KyraEngine_MR::setMouseCursor(Item item) {
int shape = 0;
int hotX = 1;
int hotY = 1;
- if (item != 0xFFFF) {
+ if (item != kItemNone) {
hotX = 12;
hotY = 19;
shape = item+248;
@@ -94,13 +94,13 @@ void KyraEngine_MR::setMouseCursor(uint16 item) {
void KyraEngine_MR::setItemMouseCursor() {
_mouseState = _itemInHand;
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(0, 0, _gameShapes[0]);
else
_screen->setMouseCursor(12, 19, _gameShapes[_itemInHand+248]);
}
-bool KyraEngine_MR::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
+bool KyraEngine_MR::dropItem(int unk1, Item item, int x, int y, int unk2) {
if (_mouseState <= -1)
return false;
@@ -123,7 +123,7 @@ bool KyraEngine_MR::dropItem(int unk1, uint16 item, int x, int y, int unk2) {
return false;
}
-bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2) {
+bool KyraEngine_MR::processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2) {
int itemPos = checkItemCollision(x, y);
if (unk1)
@@ -138,7 +138,7 @@ bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, i
if (unk2 != 3) {
for (int i = 0; i < 50; ++i) {
- if (_itemList[i].id == 0xFFFF) {
+ if (_itemList[i].id == kItemNone) {
freeItemSlot = i;
break;
}
@@ -227,7 +227,7 @@ bool KyraEngine_MR::processItemDrop(uint16 sceneId, uint16 item, int x, int y, i
return true;
}
-void KyraEngine_MR::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item, int remove) {
+void KyraEngine_MR::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item, int remove) {
if (startX == dstX && startY == dstY) {
_itemList[itemSlot].x = dstX;
_itemList[itemSlot].y = dstY;
@@ -323,7 +323,7 @@ void KyraEngine_MR::exchangeMouseItem(int itemPos, int runScript) {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
- int itemId = _itemList[itemPos].id;
+ Item itemId = _itemList[itemPos].id;
_itemList[itemPos].id = _itemInHand;
_itemInHand = itemId;
@@ -353,8 +353,8 @@ bool KyraEngine_MR::pickUpItem(int x, int y, int runScript) {
} else {
_screen->hideMouse();
deleteItemAnimEntry(itemPos);
- int itemId = _itemList[itemPos].id;
- _itemList[itemPos].id = 0xFFFF;
+ Item itemId = _itemList[itemPos].id;
+ _itemList[itemPos].id = kItemNone;
snd_playSoundEffect(0x0B, 0xC8);
setMouseCursor(itemId);
int itemString = 0;
@@ -387,8 +387,9 @@ bool KyraEngine_MR::isDropable(int x, int y) {
return true;
}
-bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
- uint16 item = _itemList[itemSlot].id;
+bool KyraEngine_MR::itemListMagic(Item handItem, int itemSlot) {
+ Item item = _itemList[itemSlot].id;
+
if (_currentChapter == 1 && handItem == 3 && item == 3 && queryGameFlag(0x76)) {
eelScript();
return true;
@@ -410,7 +411,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
}
deleteItemAnimEntry(itemSlot);
- _itemList[itemSlot].id = 0xFFFF;
+ _itemList[itemSlot].id = kItemNone;
_screen->showMouse();
return true;
}
@@ -430,7 +431,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
}
for (int i = 0; _itemMagicTable[i] != 0xFF; i += 4) {
- if (_itemMagicTable[i+0] != handItem || _itemMagicTable[i+1] != item)
+ if (_itemMagicTable[i+0] != handItem || (int8)_itemMagicTable[i+1] != item)
continue;
uint8 resItem = _itemMagicTable[i+2];
@@ -438,7 +439,7 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
snd_playSoundEffect(0x0F, 0xC8);
- _itemList[itemSlot].id = (resItem == 0xFF) ? 0xFFFF : resItem;
+ _itemList[itemSlot].id = (int8)resItem;
_screen->hideMouse();
deleteItemAnimEntry(itemSlot);
@@ -465,8 +466,9 @@ bool KyraEngine_MR::itemListMagic(int handItem, int itemSlot) {
return false;
}
-bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
- uint16 item = _mainCharacter.inventory[invSlot];
+bool KyraEngine_MR::itemInventoryMagic(Item handItem, int invSlot) {
+ Item item = _mainCharacter.inventory[invSlot];
+
if (_currentChapter == 1 && handItem == 3 && item == 3 && queryGameFlag(0x76)) {
eelScript();
return true;
@@ -482,7 +484,7 @@ bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
delay(1*_tickLength, true);
}
- _mainCharacter.inventory[invSlot] = 0xFFFF;
+ _mainCharacter.inventory[invSlot] = kItemNone;
clearInventorySlot(invSlot, 0);
_screen->showMouse();
return true;
@@ -497,7 +499,7 @@ bool KyraEngine_MR::itemInventoryMagic(int handItem, int invSlot) {
snd_playSoundEffect(0x0F, 0xC8);
- _mainCharacter.inventory[invSlot] = (resItem == 0xFF) ? 0xFFFF : resItem;
+ _mainCharacter.inventory[invSlot] = (int8)resItem;
_screen->hideMouse();
clearInventorySlot(invSlot, 0);
diff --git a/engines/kyra/items_v2.cpp b/engines/kyra/items_v2.cpp
index 29901b2ddb..90b6194f0d 100644
--- a/engines/kyra/items_v2.cpp
+++ b/engines/kyra/items_v2.cpp
@@ -31,9 +31,9 @@ namespace Kyra {
void KyraEngine_v2::initItemList(int size) {
delete[] _itemList;
- _itemList = new Item[size];
+ _itemList = new ItemDefinition[size];
assert(_itemList);
- memset(_itemList, 0, sizeof(Item)*size);
+ memset(_itemList, 0, sizeof(ItemDefinition)*size);
_itemListSize = size;
resetItemList();
@@ -41,7 +41,7 @@ void KyraEngine_v2::initItemList(int size) {
int KyraEngine_v2::findFreeItem() {
for (int i = 0; i < _itemListSize; ++i) {
- if (_itemList[i].id == 0xFFFF)
+ if (_itemList[i].id == kItemNone)
return i;
}
return -1;
@@ -50,13 +50,13 @@ int KyraEngine_v2::findFreeItem() {
int KyraEngine_v2::countAllItems() {
int num = 0;
for (int i = 0; i < _itemListSize; ++i) {
- if (_itemList[i].id != 0xFFFF)
+ if (_itemList[i].id != kItemNone)
++num;
}
return num;
}
-int KyraEngine_v2::findItem(uint16 sceneId, uint16 id) {
+int KyraEngine_v2::findItem(uint16 sceneId, Item id) {
for (int i = 0; i < _itemListSize; ++i) {
if (_itemList[i].id == id && _itemList[i].sceneId == sceneId)
return i;
@@ -64,7 +64,7 @@ int KyraEngine_v2::findItem(uint16 sceneId, uint16 id) {
return -1;
}
-int KyraEngine_v2::findItem(uint16 item) {
+int KyraEngine_v2::findItem(Item item) {
for (int i = 0; i < _itemListSize; ++i) {
if (_itemList[i].id == item)
return i;
@@ -78,17 +78,17 @@ void KyraEngine_v2::resetItemList() {
}
void KyraEngine_v2::resetItem(int index) {
- _itemList[index].id = 0xFFFF;
+ _itemList[index].id = kItemNone;
_itemList[index].sceneId = 0xFFFF;
_itemList[index].x = 0;
_itemList[index].y = 0;
}
-void KyraEngine_v2::setHandItem(uint16 item) {
+void KyraEngine_v2::setHandItem(Item item) {
Screen *scr = screen();
scr->hideMouse();
- if (item == 0xFFFF) {
+ if (item == kItemNone) {
removeHandItem();
} else {
setMouseCursor(item);
@@ -102,8 +102,8 @@ void KyraEngine_v2::removeHandItem() {
Screen *scr = screen();
scr->hideMouse();
scr->setMouseCursor(0, 0, getShapePtr(0));
- _itemInHand = -1;
- _mouseState = -1;
+ _itemInHand = kItemNone;
+ _mouseState = kItemNone;
scr->showMouse();
}
diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp
index 0fafaa15ce..5c471a8c8b 100644
--- a/engines/kyra/kyra_hof.cpp
+++ b/engines/kyra/kyra_hof.cpp
@@ -74,7 +74,7 @@ KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEn
_mainCharX = _mainCharY = -1;
_drawNoShapeFlag = false;
_charPalEntry = 0;
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_unkSceneScreenFlag1 = false;
_noScriptEnter = true;
_currentChapter = 0;
@@ -441,7 +441,7 @@ void KyraEngine_HoF::startup() {
if (_gameToLoad == -1) {
snd_playWanderScoreViaMap(52, 1);
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
- saveGameState(0, "New Game", 0);
+ saveGameStateIntern(0, "New Game", 0);
} else {
loadGameStateCheck(_gameToLoad);
}
@@ -507,7 +507,7 @@ void KyraEngine_HoF::runLoop() {
update();
if (inputFlag == 198 || inputFlag == 199) {
- _unk3 = _mouseState;
+ _savedMouseState = _mouseState;
handleInput(_mouseX, _mouseY);
}
@@ -528,7 +528,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
if (!_screen->isMouseVisible())
return;
- if (_unk3 == -2) {
+ if (_savedMouseState == -2) {
snd_playSoundEffect(13);
return;
}
@@ -537,8 +537,8 @@ void KyraEngine_HoF::handleInput(int x, int y) {
if (x <= 6 || x >= 312 || y <= 6 || y >= 135) {
bool exitOk = false;
- assert(_unk3 + 6 >= 0);
- switch (_unk3 + 6) {
+ assert(_savedMouseState + 6 >= 0);
+ switch (_savedMouseState + 6) {
case 0:
if (_sceneExit1 != 0xFFFF)
exitOk = true;
@@ -569,7 +569,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
}
}
- if (checkCharCollision(x, y) && _unk3 >= -1) {
+ if (checkCharCollision(x, y) && _savedMouseState >= -1) {
runSceneScript2();
return;
} else if (pickUpItem(x, y)) {
@@ -609,7 +609,7 @@ void KyraEngine_HoF::handleInput(int x, int y) {
dropItem(0, _itemInHand, x, y, 1);
} else {
- if (_unk3 == -2 || y > 135)
+ if (_savedMouseState == -2 || y > 135)
return;
if (!_unk5) {
@@ -790,7 +790,7 @@ void KyraEngine_HoF::updateMouse() {
if ((mouse.y > 145) || (mouse.x > 6 && mouse.x < 312 && mouse.y > 6 && mouse.y < 135)) {
_mouseState = _itemInHand;
_screen->hideMouse();
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(0, 0, getShapePtr(0));
else
_screen->setMouseCursor(8, 15, getShapePtr(_itemInHand+64));
@@ -1169,25 +1169,25 @@ int KyraEngine_HoF::inputSceneChange(int x, int y, int unk1, int unk2) {
_pathfinderFlag = 15;
if (!_unkHandleSceneChangeFlag) {
- if (_unk3 == -3) {
+ if (_savedMouseState == -3) {
if (_sceneList[curScene].exit4 != 0xFFFF) {
x = 4;
y = _sceneEnterY4;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -5) {
+ } else if (_savedMouseState == -5) {
if (_sceneList[curScene].exit2 != 0xFFFF) {
x = 316;
y = _sceneEnterY2;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -6) {
+ } else if (_savedMouseState == -6) {
if (_sceneList[curScene].exit1 != 0xFFFF) {
x = _sceneEnterX1;
y = _sceneEnterY1 - 2;
_pathfinderFlag = 14;
}
- } else if (_unk3 == -4) {
+ } else if (_savedMouseState == -4) {
if (_sceneList[curScene].exit3 != 0xFFFF) {
x = _sceneEnterX3;
y = 147;
@@ -1200,13 +1200,13 @@ int KyraEngine_HoF::inputSceneChange(int x, int y, int unk1, int unk2) {
int vocH = _flags.isTalkie ? 131 : -1;
if (_pathfinderFlag) {
- if (findItem(curScene, 13) >= 0 && _unk3 <= -3) {
+ if (findItem(curScene, 13) >= 0 && _savedMouseState <= -3) {
strId = 252;
} else if (_itemInHand == 72) {
strId = 257;
- } else if (findItem(curScene, 72) >= 0 && _unk3 <= -3) {
+ } else if (findItem(curScene, 72) >= 0 && _savedMouseState <= -3) {
strId = 256;
- } else if (getInventoryItemSlot(72) != -1 && _unk3 <= -3) {
+ } else if (getInventoryItemSlot(72) != -1 && _savedMouseState <= -3) {
strId = 257;
}
}
diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h
index 05d04fe993..576232740b 100644
--- a/engines/kyra/kyra_hof.h
+++ b/engines/kyra/kyra_hof.h
@@ -443,16 +443,16 @@ protected:
bool lineIsPassable(int x, int y);
// item
- void setMouseCursor(uint16 item);
+ void setMouseCursor(Item item);
uint8 _itemHtDat[176];
int checkItemCollision(int x, int y);
void updateWaterFlasks();
- bool dropItem(int unk1, uint16 item, int x, int y, int unk2);
- bool processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2);
- void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item);
+ bool dropItem(int unk1, Item item, int x, int y, int unk2);
+ bool processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2);
+ void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item);
void exchangeMouseItem(int itemPos);
bool pickUpItem(int x, int y);
@@ -461,18 +461,18 @@ protected:
static const byte _itemStringMap[];
static const int _itemStringMapSize;
- static const int16 _flaskTable[];
- bool itemIsFlask(int item);
+ static const Item _flaskTable[];
+ bool itemIsFlask(Item item);
// inventory
static const int _inventoryX[];
static const int _inventoryY[];
static const uint16 _itemMagicTable[];
- int getInventoryItemSlot(uint16 item);
+ int getInventoryItemSlot(Item item);
void removeSlotFromInventory(int slot);
- bool checkInventoryItemExchange(uint16 item, int slot);
- void drawInventoryShape(int page, uint16 item, int slot);
+ bool checkInventoryItemExchange(Item item, int slot);
+ void drawInventoryShape(int page, Item item, int slot);
void clearInventorySlot(int slot, int page);
void redrawInventory(int page);
void scrollInventoryWheel();
@@ -561,9 +561,9 @@ protected:
void changeFileExtension(char *buffer);
// - Just used in French version
- int getItemCommandStringDrop(uint16 item);
- int getItemCommandStringPickUp(uint16 item);
- int getItemCommandStringInv(uint16 item);
+ int getItemCommandStringDrop(Item item);
+ int getItemCommandStringPickUp(Item item);
+ int getItemCommandStringInv(Item item);
// -
char _internStringBuf[200];
@@ -915,7 +915,7 @@ protected:
int _dbgPass;
// save/load specific
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
};
diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp
index 159230e928..d46fc2d502 100644
--- a/engines/kyra/kyra_lok.cpp
+++ b/engines/kyra/kyra_lok.cpp
@@ -247,7 +247,7 @@ Common::Error KyraEngine_LoK::init() {
_brandonPosX = _brandonPosY = -1;
_poisonDeathCounter = 0;
- memset(_itemTable, 0, sizeof(_itemTable));
+ memset(_itemHtDat, 0, sizeof(_itemHtDat));
memset(_exitList, 0xFFFF, sizeof(_exitList));
_exitListPtr = 0;
_pathfinderFlag = _pathfinderFlag2 = 0;
@@ -260,7 +260,7 @@ Common::Error KyraEngine_LoK::init() {
_marbleVaseItem = -1;
memset(_foyerItemTable, -1, sizeof(_foyerItemTable));
- _itemInHand = -1;
+ _itemInHand = kItemNone;
_currentRoom = 0xFFFF;
_scenePhasingFlag = 0;
@@ -373,7 +373,7 @@ void KyraEngine_LoK::startup() {
for (int i = 0; i < _roomTableSize; ++i) {
for (int item = 0; item < 12; ++item) {
- _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsTable[item] = kItemNone;
_roomTable[i].itemsXPos[item] = 0xFFFF;
_roomTable[i].itemsYPos[item] = 0xFF;
_roomTable[i].needInit[item] = 0;
@@ -420,7 +420,7 @@ void KyraEngine_LoK::startup() {
_gui->buttonMenuCallback(0);
_menuDirectlyToLoad = false;
} else if (!shouldQuit()) {
- saveGameState(0, "New game", 0);
+ saveGameStateIntern(0, "New game", 0);
}
} else {
_screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
@@ -672,12 +672,13 @@ void KyraEngine_LoK::processInput(int xpos, int ypos) {
runNpcScript(script);
return;
}
- if (_itemInHand != -1) {
+ if (_itemInHand != kItemNone) {
if (ypos < 155) {
if (hasClickedOnExit(xpos, ypos)) {
handleSceneChange(xpos, ypos, 1, 1);
return;
}
+
dropItem(0, _itemInHand, xpos, ypos, 1);
}
} else {
@@ -691,14 +692,14 @@ void KyraEngine_LoK::processInput(int xpos, int ypos) {
int KyraEngine_LoK::processInputHelper(int xpos, int ypos) {
uint8 item = findItemAtPos(xpos, ypos);
if (item != 0xFF) {
- if (_itemInHand == -1) {
+ if (_itemInHand == kItemNone) {
_screen->hideMouse();
_animator->animRemoveGameItem(item);
snd_playSoundEffect(53);
assert(_currentCharacter->sceneId < _roomTableSize);
Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
int item2 = currentRoom->itemsTable[item];
- currentRoom->itemsTable[item] = 0xFF;
+ currentRoom->itemsTable[item] = kItemNone;
setMouseItem(item2);
assert(_itemList && _takenList);
updateSentenceCommand(_itemList[getItemListIndex(item2)], _takenList[0], 179);
@@ -830,7 +831,7 @@ void KyraEngine_LoK::updateMousePointer(bool forceUpdate) {
if (mouse.y > 158 || (mouse.x >= 12 && mouse.x < 308 && mouse.y < 136 && mouse.y >= 12) || forceUpdate) {
_mouseState = _itemInHand;
_screen->hideMouse();
- if (_itemInHand == -1)
+ if (_itemInHand == kItemNone)
_screen->setMouseCursor(1, 1, _shapes[0]);
else
_screen->setMouseCursor(8, 15, _shapes[216+_itemInHand]);
diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h
index 50f36d7b71..dfbf5bddd8 100644
--- a/engines/kyra/kyra_lok.h
+++ b/engines/kyra/kyra_lok.h
@@ -30,6 +30,7 @@
#include "kyra/script.h"
#include "kyra/screen_lok.h"
#include "kyra/gui_lok.h"
+#include "kyra/item.h"
namespace Kyra {
@@ -46,7 +47,7 @@ struct Character {
uint8 height;
uint8 facing;
uint16 currentAnimFrame;
- uint8 inventoryItems[10];
+ int8 inventoryItems[10];
int16 x1, y1, x2, y2;
};
@@ -62,19 +63,12 @@ struct Room {
uint16 eastExit;
uint16 southExit;
uint16 westExit;
- uint8 itemsTable[12];
+ int8 itemsTable[12];
uint16 itemsXPos[12];
uint8 itemsYPos[12];
uint8 needInit[12];
};
-struct Item {
- uint8 unk1;
- uint8 height;
- uint8 unk2;
- uint8 unk3;
-};
-
struct SeqLoop {
const uint8 *ptr;
uint16 count;
@@ -218,7 +212,7 @@ public:
protected:
int32 _speechPlayTime;
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
protected:
// input
@@ -288,11 +282,11 @@ protected:
void placeItemInGenericMapScene(int item, int index);
// -> mouse item
- void setHandItem(uint16 item);
+ void setHandItem(Item item);
void removeHandItem();
- void setMouseItem(uint16 item);
+ void setMouseItem(Item item);
- int getItemListIndex(uint16 item);
+ int getItemListIndex(Item item);
// -> graphics effects
void wipeDownMouseItem(int xpos, int ypos);
@@ -402,7 +396,7 @@ protected:
bool _menuDirectlyToLoad;
uint8 *_itemBkgBackUp[2];
uint8 *_shapes[373];
- int8 _itemInHand;
+ Item _itemInHand;
bool _changedScene;
int _unkScreenVar1, _unkScreenVar2, _unkScreenVar3;
int _beadStateVar;
@@ -455,7 +449,7 @@ protected:
int8 *_sceneAnimTable[50];
- Item _itemTable[145];
+ uint8 _itemHtDat[145];
int _lastProcessedItem;
int _lastProcessedItemHeight;
diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp
index c224a76385..5ac6da9f2b 100644
--- a/engines/kyra/kyra_mr.cpp
+++ b/engines/kyra/kyra_mr.cpp
@@ -99,8 +99,8 @@ KyraEngine_MR::KyraEngine_MR(OSystem *system, const GameFlags &flags) : KyraEngi
_unk5 = 0;
_unkSceneScreenFlag1 = false;
_noScriptEnter = true;
- _itemInHand = _mouseState = -1;
- _unk3 = -1;
+ _itemInHand = _mouseState = kItemNone;
+ _savedMouseState = -1;
_unk4 = 0;
_loadingState = false;
_noStartupChat = false;
@@ -653,7 +653,7 @@ void KyraEngine_MR::startup() {
assert(_invWsa);
_invWsa->open("MOODOMTR.WSA", 1, 0);
_invWsaFrame = 6;
- saveGameState(0, "New Game", 0);
+ saveGameStateIntern(0, "New Game", 0);
if (_gameToLoad == -1)
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
else
@@ -966,7 +966,7 @@ void KyraEngine_MR::runLoop() {
_timer->update();
if (inputFlag == 198 || inputFlag == 199) {
- _unk3 = _mouseState;
+ _savedMouseState = _mouseState;
Common::Point mouse = getMousePos();
handleInput(mouse.x, mouse.y);
}
@@ -988,7 +988,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
if (!_screen->isMouseVisible())
return;
- if (_unk3 == -3) {
+ if (_savedMouseState == -3) {
snd_playSoundEffect(0x0D, 0x80);
return;
}
@@ -997,7 +997,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
int skip = 0;
- if (checkCharCollision(x, y) && _unk3 >= -1 && runSceneScript2()) {
+ if (checkCharCollision(x, y) && _savedMouseState >= -1 && runSceneScript2()) {
return;
} else if (_itemInHand != 27 && pickUpItem(x, y, 1)) {
return;
@@ -1023,7 +1023,7 @@ void KyraEngine_MR::handleInput(int x, int y) {
if (checkCharCollision(x, y)) {
if (runSceneScript2())
return;
- } else if (_itemInHand >= 0 && _unk3 >= 0) {
+ } else if (_itemInHand >= 0 && _savedMouseState >= 0) {
if (_itemInHand == 27) {
makeCharFacingMouse();
} else if (y <= 187) {
@@ -1033,10 +1033,10 @@ void KyraEngine_MR::handleInput(int x, int y) {
dropItem(0, _itemInHand, x, y, 1);
}
return;
- } else if (_unk3 == -3) {
+ } else if (_savedMouseState == -3) {
return;
} else {
- if (y > 187 && _unk3 > -4)
+ if (y > 187 && _savedMouseState > -4)
return;
if (_unk5) {
_unk5 = 0;
@@ -1052,25 +1052,25 @@ int KyraEngine_MR::inputSceneChange(int x, int y, int unk1, int unk2) {
_pathfinderFlag = 15;
if (!_unkHandleSceneChangeFlag) {
- if (_unk3 == -4) {
+ if (_savedMouseState == -4) {
if (_sceneList[curScene].exit4 != 0xFFFF) {
x = 4;
y = _sceneEnterY4;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -6) {
+ } else if (_savedMouseState == -6) {
if (_sceneList[curScene].exit2 != 0xFFFF) {
x = 316;
y = _sceneEnterY2;
_pathfinderFlag = 7;
}
- } else if (_unk3 == -7) {
+ } else if (_savedMouseState == -7) {
if (_sceneList[curScene].exit1 != 0xFFFF) {
x = _sceneEnterX1;
y = _sceneEnterY1 - 2;
_pathfinderFlag = 14;
}
- } else if (_unk3 == -5) {
+ } else if (_savedMouseState == -5) {
if (_sceneList[curScene].exit3 != 0xFFFF) {
x = _sceneEnterX3;
y = 191;
@@ -1167,8 +1167,8 @@ void KyraEngine_MR::updateMouse() {
}
if (hasItemCollision && _mouseState < -1 && _itemInHand < 0) {
- _mouseState = -1;
- _itemInHand = -1;
+ _mouseState = kItemNone;
+ _itemInHand = kItemNone;
_screen->setMouseCursor(0, 0, _gameShapes[0]);
}
diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h
index 36b937f2a8..e6f75742f4 100644
--- a/engines/kyra/kyra_mr.h
+++ b/engines/kyra/kyra_mr.h
@@ -235,7 +235,7 @@ private:
void showMessage(const char *string, uint8 c0, uint8 c1);
void showMessageFromCCode(int string, uint8 c0, int);
- void updateItemCommand(int item, int str, uint8 c0);
+ void updateItemCommand(Item item, int str, uint8 c0);
void updateCommandLine();
void restoreCommandLine();
@@ -262,7 +262,7 @@ private:
static const uint8 _inventoryY[];
void redrawInventory(int page);
void clearInventorySlot(int slot, int page);
- void drawInventorySlot(int page, int item, int slot);
+ void drawInventorySlot(int page, Item item, int slot);
WSAMovie_v2 *_invWsa;
int _invWsaFrame;
@@ -284,24 +284,24 @@ private:
int8 *_itemBuffer1;
int8 *_itemBuffer2;
- static const uint8 _trashItemList[];
+ static const Item _trashItemList[];
void removeTrashItems();
void initItems();
int checkItemCollision(int x, int y);
- bool dropItem(int unk1, uint16 item, int x, int y, int unk2);
- bool processItemDrop(uint16 sceneId, uint16 item, int x, int y, int unk1, int unk2);
- void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, uint16 item, int remove);
+ bool dropItem(int unk1, Item item, int x, int y, int unk2);
+ bool processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2);
+ void itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item, int remove);
void exchangeMouseItem(int itemPos, int runScript);
bool pickUpItem(int x, int y, int runScript);
bool isDropable(int x, int y);
const uint8 *_itemMagicTable;
- bool itemListMagic(int handItem, int itemSlot);
- bool itemInventoryMagic(int handItem, int invSlot);
+ bool itemListMagic(Item handItem, int itemSlot);
+ bool itemInventoryMagic(Item handItem, int invSlot);
const uint8 *_itemStringMap;
int _itemStringMapSize;
@@ -315,7 +315,7 @@ private:
// -> hand item
void setItemMouseCursor();
- void setMouseCursor(uint16 item);
+ void setMouseCursor(Item item);
// shapes
void initMouseShapes();
@@ -585,7 +585,7 @@ private:
int albumClose(Button *caller);
// save/load
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Common::Error loadGameState(int slot);
// opcodes
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 1c27716a67..26c4001578 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -103,11 +103,14 @@ Common::Error KyraEngine_v1::init() {
syncSoundSettings();
if (!_flags.useDigSound) {
- // We prefer AdLib over MIDI in Kyra 1, since it offers MT-32 support only, most users don't have a real
- // MT-32/LAPC1/CM32L/CM64 device and AdLib sounds better than our incomplete MT-32 emulator and also better than
- // MT-32/GM mapping. For Kyra 2 and LoL which have real GM tracks which sound better than AdLib tracks we prefer GM
- // since most users have a GM compatible device.
- MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : 0));
+ // In Kyra 1 users who have specified a default MT-32 device in the launcher settings
+ // will get MT-32 music, otherwise AdLib. In Kyra 2 and LoL users who have specified a
+ // default GM device in the launcher will get GM music, otherwise AdLib. Users who want
+ // MT-32 music in Kyra2 or LoL have to select this individually (since we assume that
+ // most users rather have a GM device than a MT-32 device).
+ // Users who want PC speaker sound always have to select this individually for all
+ // Kyra games.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : MDT_PREFER_MT32));
if (_flags.platform == Common::kPlatformFMTowns) {
if (_flags.gameID == GI_KYRA1)
@@ -179,7 +182,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
}
@@ -275,7 +278,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
} else {
char savegameName[14];
sprintf(savegameName, "Quicksave %d", event.kbd.keycode - Common::KEYCODE_0);
- saveGameState(saveLoadSlot, savegameName, 0);
+ saveGameStateIntern(saveLoadSlot, savegameName, 0);
}
} else if (event.kbd.hasFlags(Common::KBD_CTRL)) {
if (event.kbd.keycode == Common::KEYCODE_d) {
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index d077d3a3b0..31c07336a6 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -37,6 +37,7 @@
#include "sound/mixer.h"
#include "kyra/script.h"
+#include "kyra/item.h"
namespace Common {
class SeekableReadStream;
@@ -93,7 +94,7 @@ class KyraMetaEngine;
* is pretty minor priority though, since the benefit would be mostly nicer code). The biggest
* task left is the kyra.dat handling, which is currently being revised by LordHoto.
*
- * Supported games:
+ * Games using this engine:
* - The Legend of Kyrandia (fully supported, except for Macintosh port, which lacks sound)
* - (The) Hand of Fate (fully supported)
* - Malcolm's Revenge (fully supported)
@@ -339,7 +340,7 @@ protected:
// items
int _mouseState;
- virtual void setHandItem(uint16 item) = 0;
+ virtual void setHandItem(Item item) = 0;
virtual void removeHandItem() = 0;
// game flags
@@ -414,8 +415,8 @@ protected:
void loadGameStateCheck(int slot);
virtual Common::Error loadGameState(int slot) = 0;
- Common::Error saveGameState(int slot, const char *saveName) { return saveGameState(slot, saveName, 0); }
- virtual Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
+ Common::Error saveGameState(int slot, const char *saveName) { return saveGameStateIntern(slot, saveName, 0); }
+ virtual Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0;
Common::SeekableReadStream *openSaveForReading(const char *filename, SaveHeader &header);
Common::WriteStream *openSaveForWriting(const char *filename, const char *saveName, const Graphics::Surface *thumbnail) const;
diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h
index 6414040344..39c9f0875e 100644
--- a/engines/kyra/kyra_v2.h
+++ b/engines/kyra/kyra_v2.h
@@ -29,6 +29,7 @@
#include "kyra/kyra_v1.h"
#include "kyra/gui.h"
#include "kyra/wsamovie.h"
+#include "kyra/item.h"
#include "common/list.h"
#include "common/hashmap.h"
@@ -41,7 +42,7 @@ struct FrameControl {
};
struct ItemAnimData_v2 {
- int16 itemIndex;
+ Item itemIndex;
uint8 numFrames;
const FrameControl *frames;
};
@@ -69,7 +70,7 @@ public:
int animScriptFrameAdd;
// Item specific
- int maxItemId;
+ Item maxItemId;
};
KyraEngine_v2(OSystem *system, const GameFlags &flags, const EngineDesc &desc);
@@ -286,8 +287,8 @@ protected:
int _pathfinderPositionIndexTable[200];
// items
- struct Item {
- uint16 id;
+ struct ItemDefinition {
+ Item id;
uint16 sceneId;
int16 x;
uint8 y;
@@ -295,25 +296,26 @@ protected:
void initItemList(int size);
- uint16 _hiddenItems[100];
+ Item _hiddenItems[100];
- Item *_itemList;
+ ItemDefinition *_itemList;
int _itemListSize;
int _itemInHand;
+ int _savedMouseState;
int findFreeItem();
int countAllItems();
- int findItem(uint16 sceneId, uint16 id);
- int findItem(uint16 item);
+ int findItem(uint16 sceneId, Item id);
+ int findItem(Item item);
void resetItemList();
void resetItem(int index);
- virtual void setMouseCursor(uint16 item) = 0;
+ virtual void setMouseCursor(Item item) = 0;
- void setHandItem(uint16 item);
+ void setHandItem(Item item);
void removeHandItem();
// character
@@ -324,7 +326,7 @@ protected:
uint8 facing;
uint16 animFrame;
byte walkspeed;
- uint16 inventory[20];
+ Item inventory[20];
int16 x1, y1;
int16 x2, y2;
int16 x3, y3;
@@ -360,7 +362,7 @@ protected:
virtual void randomSceneChat() = 0;
// unknown
- int _unk3, _unk4, _unk5;
+ int _unk4, _unk5;
bool _unkSceneScreenFlag1;
bool _unkHandleSceneChangeFlag;
diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp
index 03d52ec4ac..f6ac8a1a73 100644
--- a/engines/kyra/lol.cpp
+++ b/engines/kyra/lol.cpp
@@ -1930,11 +1930,11 @@ int LoLEngine::playCharacterScriptChat(int charId, int mode, int restorePortrait
return 1;
}
-void LoLEngine::giveItemToMonster(MonsterInPlay *monster, uint16 item) {
+void LoLEngine::giveItemToMonster(MonsterInPlay *monster, Item item) {
uint16 *c = &monster->assignedItems;
while (*c)
c = &_itemsInPlay[*c].nextAssignedObject;
- *c = item;
+ *c = (uint16)item;
_itemsInPlay[item].nextAssignedObject = 0;
}
diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h
index 57c127a94f..f2c93f83fc 100644
--- a/engines/kyra/lol.h
+++ b/engines/kyra/lol.h
@@ -225,7 +225,7 @@ struct FlyingObject {
uint8 enable;
uint8 objectType;
uint16 attackerId;
- uint16 item;
+ Item item;
uint16 x;
uint16 y;
uint8 flyingHeight;
@@ -1202,19 +1202,19 @@ private:
// items
void giveCredits(int credits, int redraw);
void takeCredits(int credits, int redraw);
- int makeItem(int itemType, int curFrame, int flags);
- void placeMoveLevelItem(int itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight);
- bool addItemToInventory(int itemIndex);
- bool testUnkItemFlags(int itemIndex);
- void deleteItem(int itemIndex);
+ Item makeItem(int itemType, int curFrame, int flags);
+ void placeMoveLevelItem(Item itemIndex, int level, int block, int xOffs, int yOffs, int flyingHeight);
+ bool addItemToInventory(Item itemIndex);
+ bool testUnkItemFlags(Item itemIndex);
+ void deleteItem(Item itemIndex);
ItemInPlay *findObject(uint16 index);
- void runItemScript(int charNum, int item, int flags, int next, int reg4);
- void setHandItem(uint16 itemIndex);
+ void runItemScript(int charNum, Item item, int flags, int next, int reg4);
+ void setHandItem(Item itemIndex);
bool itemEquipped(int charNum, uint16 itemType);
- void setItemPosition(int item, uint16 x, uint16 y, int flyingHeight, int b);
- void removeLevelItem(int item, int block);
- bool launchObject(int objectType, int item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c);
+ void setItemPosition(Item item, uint16 x, uint16 y, int flyingHeight, int b);
+ void removeLevelItem(Item item, int block);
+ bool launchObject(int objectType, Item item, int startX, int startY, int flyingHeight, int direction, int, int attackerId, int c);
void endObjectFlight(FlyingObject *t, int x, int y, int objectOnNextBlock);
void processObjectFlight(FlyingObject *t, int x, int y);
void updateObjectFlightPosition(FlyingObject *t);
@@ -1231,9 +1231,9 @@ private:
ItemInPlay *_itemsInPlay;
ItemProperty *_itemProperties;
- int _itemInHand;
- uint16 _inventory[48];
- int _inventoryCurItem;
+ Item _itemInHand;
+ Item _inventory[48];
+ Item _inventoryCurItem;
int _currentControlMode;
int _specialSceneFlag;
int _lastCharInventory;
@@ -1269,10 +1269,10 @@ private:
int calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
void setMonsterDirection(MonsterInPlay *monster, int dir);
void monsterDropItems(MonsterInPlay *monster);
- void removeAssignedObjectFromBlock(LevelBlockProperty *l, int id);
- void removeDrawObjectFromBlock(LevelBlockProperty *l, int id);
- void assignMonsterToBlock(uint16 *assignedBlockObjects, int id);
- void giveItemToMonster(MonsterInPlay *monster, uint16 item);
+ void removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id);
+ void removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id);
+ void assignMonsterToBlock(uint16 *assignedBlockObjects, uint16 id);
+ void giveItemToMonster(MonsterInPlay *monster, Item item);
int checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 objectWidth, uint16 testFlag, uint16 wallFlag);
int checkBlockForWallsAndSufficientSpace(int block, int x, int y, int objectWidth, int testFlag, int wallFlag);
int calcMonsterSkillLevel(int id, int a);
@@ -1488,7 +1488,7 @@ private:
// save
Common::Error loadGameState(int slot);
- Common::Error saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail);
+ Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail);
Graphics::Surface *generateSaveThumbnail() const;
diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp
index 0d968de9ff..445ea579a0 100644
--- a/engines/kyra/resource_intern.cpp
+++ b/engines/kyra/resource_intern.cpp
@@ -514,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/saveload.cpp b/engines/kyra/saveload.cpp
index b1cc954137..c9450e3f27 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)
@@ -250,7 +250,7 @@ bool KyraEngine_v1::saveFileLoadable(int slot) {
void KyraEngine_v1::checkAutosave() {
if (shouldPerformAutoSave(_lastAutosave)) {
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
_lastAutosave = _system->getMillis();
}
}
diff --git a/engines/kyra/saveload_hof.cpp b/engines/kyra/saveload_hof.cpp
index 2d276cc6a4..e957718f95 100644
--- a/engines/kyra/saveload_hof.cpp
+++ b/engines/kyra/saveload_hof.cpp
@@ -35,7 +35,7 @@
namespace Kyra {
-Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_HoF::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
@@ -64,7 +64,7 @@ Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, cons
for (int i = 0; i < 25; ++i)
out->writeSint16BE(_cauldronTable[i]);
for (int i = 0; i < 20; ++i)
- out->writeUint16BE(_hiddenItems[i]);
+ out->writeSint16BE(_hiddenItems[i]);
for (int i = 0; i < 19; ++i)
out->write(_conversationState[i], 14);
out->write(_newSceneDlgState, 32);
@@ -83,7 +83,7 @@ Common::Error KyraEngine_HoF::saveGameState(int slot, const char *saveName, cons
out->writeSint16BE(_mainCharacter.y2);
for (int i = 0; i < 30; ++i) {
- out->writeUint16BE(_itemList[i].id);
+ out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeByte(_itemList[i].y);
@@ -183,7 +183,7 @@ Common::Error KyraEngine_HoF::loadGameState(int slot) {
for (int i = 0; i < 25; ++i)
_cauldronTable[i] = in.readSint16();
for (int i = 0; i < 20; ++i)
- _hiddenItems[i] = in.readUint16();
+ _hiddenItems[i] = in.readSint16();
if (header.originalSave) {
assert(sizeof(_flagsTable) >= 0x41);
@@ -222,7 +222,7 @@ Common::Error KyraEngine_HoF::loadGameState(int slot) {
_mainCharacter.y2 = in.readSint16();
for (int i = 0; i < 30; ++i) {
- _itemList[i].id = in.readUint16();
+ _itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readByte();
diff --git a/engines/kyra/saveload_lok.cpp b/engines/kyra/saveload_lok.cpp
index 1a61f9a962..3e11d3dad3 100644
--- a/engines/kyra/saveload_lok.cpp
+++ b/engines/kyra/saveload_lok.cpp
@@ -80,7 +80,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
}
_marbleVaseItem = in->readSint16BE();
- _itemInHand = in->readByte();
+ _itemInHand = (int8)in->readByte();
for (int i = 0; i < 4; ++i)
_birthstoneGemTable[i] = in->readByte();
@@ -109,7 +109,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
for (int i = 0; i < _roomTableSize; ++i) {
for (int item = 0; item < 12; ++item) {
- _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsTable[item] = kItemNone;
_roomTable[i].itemsXPos[item] = 0xFFFF;
_roomTable[i].itemsYPos[item] = 0xFF;
_roomTable[i].needInit[item] = 0;
@@ -241,7 +241,7 @@ Common::Error KyraEngine_LoK::loadGameState(int slot) {
return Common::kNoError;
}
-Common::Error KyraEngine_LoK::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_LoK::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
if (shouldQuit())
diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp
index 480714e5c9..aee24957a0 100644
--- a/engines/kyra/saveload_lol.cpp
+++ b/engines/kyra/saveload_lol.cpp
@@ -117,7 +117,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
_selectedCharacter = in.readSByte();
_currentLevel = in.readByte();
for (int i = 0; i < 48; i++)
- _inventory[i] = in.readUint16BE();
+ _inventory[i] = in.readSint16BE();
_inventoryCurItem = in.readSint16BE();
_itemInHand = in.readSint16BE();
_lastMouseRegion = in.readSint16BE();
@@ -243,7 +243,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
m->enable = in.readByte();
m->objectType = in.readByte();
m->attackerId = in.readUint16BE();
- m->item = in.readUint16BE();
+ m->item = in.readSint16BE();
m->x = in.readUint16BE();
m->y = in.readUint16BE();
m->flyingHeight = in.readByte();
@@ -274,7 +274,7 @@ Common::Error LoLEngine::loadGameState(int slot) {
return Common::kNoError;
}
-Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
+Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail);
@@ -340,7 +340,7 @@ Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Gra
out->writeSByte(_selectedCharacter);
out->writeByte(_currentLevel);
for (int i = 0; i < 48; i++)
- out->writeUint16BE(_inventory[i]);
+ out->writeSint16BE(_inventory[i]);
out->writeSint16BE(_inventoryCurItem);
out->writeSint16BE(_itemInHand);
out->writeSint16BE(_lastMouseRegion);
@@ -423,7 +423,7 @@ Common::Error LoLEngine::saveGameState(int slot, const char *saveName, const Gra
out->writeByte(m->enable);
out->writeByte(m->objectType);
out->writeUint16BE(m->attackerId);
- out->writeUint16BE(m->item);
+ out->writeSint16BE(m->item);
out->writeUint16BE(m->x);
out->writeUint16BE(m->y);
out->writeByte(m->flyingHeight);
diff --git a/engines/kyra/saveload_mr.cpp b/engines/kyra/saveload_mr.cpp
index 737c83c33d..80c9b2b83c 100644
--- a/engines/kyra/saveload_mr.cpp
+++ b/engines/kyra/saveload_mr.cpp
@@ -32,7 +32,7 @@
namespace Kyra {
-Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const Graphics::Surface *thumb) {
+Common::Error KyraEngine_MR::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumb) {
const char *fileName = getSavegameFilename(slot);
Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumb);
@@ -55,7 +55,7 @@ Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const
out->write(_conversationState[i], 30);
out->write(_newSceneDlgState, 40);
for (int i = 0; i < 100; ++i)
- out->writeUint16BE(_hiddenItems[i]);
+ out->writeSint16BE(_hiddenItems[i]);
out->write(_scoreFlagTable, 26);
out->writeUint16BE(_mainCharacter.sceneId);
@@ -74,7 +74,7 @@ Common::Error KyraEngine_MR::saveGameState(int slot, const char *saveName, const
out->writeSint16BE(_mainCharacter.y3);
for (int i = 0; i < 50; ++i) {
- out->writeUint16BE(_itemList[i].id);
+ out->writeSint16BE(_itemList[i].id);
out->writeUint16BE(_itemList[i].sceneId);
out->writeSint16BE(_itemList[i].x);
out->writeSint16BE(_itemList[i].y);
@@ -189,7 +189,7 @@ Common::Error KyraEngine_MR::loadGameState(int slot) {
}
for (int i = 0; i < 100; ++i)
- _hiddenItems[i] = in.readUint16();
+ _hiddenItems[i] = in.readSint16();
if (header.originalSave)
in.read(_flagsTable, 69);
@@ -216,7 +216,7 @@ Common::Error KyraEngine_MR::loadGameState(int slot) {
_mainCharacter.y3 = in.readSint16();
for (int i = 0; i < 50; ++i) {
- _itemList[i].id = in.readUint16();
+ _itemList[i].id = in.readSint16();
_itemList[i].sceneId = in.readUint16();
_itemList[i].x = in.readSint16();
_itemList[i].y = in.readSint16();
diff --git a/engines/kyra/scene_hof.cpp b/engines/kyra/scene_hof.cpp
index 4559554d77..79827361a3 100644
--- a/engines/kyra/scene_hof.cpp
+++ b/engines/kyra/scene_hof.cpp
@@ -241,7 +241,7 @@ void KyraEngine_HoF::enterNewSceneUnk1(int facing, int unk1, int unk2) {
}
void KyraEngine_HoF::enterNewSceneUnk2(int unk1) {
- _unk3 = -1;
+ _savedMouseState = -1;
if (_flags.isTalkie) {
if (_mainCharX == -1 && _mainCharY == -1 && _mainCharacter.sceneId != 61 &&
@@ -265,7 +265,7 @@ void KyraEngine_HoF::enterNewSceneUnk2(int unk1) {
}
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
}
int KyraEngine_HoF::trySceneChange(int *moveTable, int unk1, int updateChar) {
@@ -339,16 +339,16 @@ int KyraEngine_HoF::checkSceneChange() {
int facing = 0;
int process = 0;
- if (_screen->getLayer(charX, charY) == 1 && _unk3 == -6) {
+ if (_screen->getLayer(charX, charY) == 1 && _savedMouseState == -6) {
facing = 0;
process = 1;
- } else if (charX >= 316 && _unk3 == -5) {
+ } else if (charX >= 316 && _savedMouseState == -5) {
facing = 2;
process = 1;
- } else if (charY >= 142 && _unk3 == -4) {
+ } else if (charY >= 142 && _savedMouseState == -4) {
facing = 4;
process = 1;
- } else if (charX <= 4 && _unk3 == -3) {
+ } else if (charX <= 4 && _savedMouseState == -3) {
facing = 6;
process = 1;
}
diff --git a/engines/kyra/scene_lok.cpp b/engines/kyra/scene_lok.cpp
index f71c3bd756..f7ada5d623 100644
--- a/engines/kyra/scene_lok.cpp
+++ b/engines/kyra/scene_lok.cpp
@@ -824,13 +824,14 @@ void KyraEngine_LoK::initSceneScreen(int brandonAlive) {
_emc->run(&_scriptClick);
setTextFadeTimerCountdown(-1);
+
if (_currentCharacter->sceneId == 210) {
- if (_itemInHand != -1)
+ if (_itemInHand != kItemNone)
magicOutMouseItem(2, -1);
_screen->hideMouse();
for (int i = 0; i < 10; ++i) {
- if (_currentCharacter->inventoryItems[i] != 0xFF)
+ if (_currentCharacter->inventoryItems[i] != kItemNone)
magicOutMouseItem(2, i);
}
_screen->showMouse();
diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp
index bf3320486a..e070b91a44 100644
--- a/engines/kyra/scene_lol.cpp
+++ b/engines/kyra/scene_lol.cpp
@@ -1618,7 +1618,7 @@ void LoLEngine::generateBlockDrawingBuffer() {
_sceneDrawVarLeft = _dscBlockMap[_currentDirection + 8];
/*******************************************
- * _visibleBlocks map *
+ * _visibleBlocks map *
* *
* | | | | | | *
* 00 | 01 | 02 | 03 | 04 | 05 | 06 *
diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp
index bd0a1fe544..f1ea79f49b 100644
--- a/engines/kyra/scene_mr.cpp
+++ b/engines/kyra/scene_mr.cpp
@@ -176,8 +176,8 @@ void KyraEngine_MR::enterNewScene(uint16 sceneId, int facing, int unk1, int unk2
setNextIdleAnimTimer();
if (_itemInHand < 0) {
- _itemInHand = -1;
- _mouseState = -1;
+ _itemInHand = kItemNone;
+ _mouseState = kItemNone;
_screen->setMouseCursor(0, 0, _gameShapes[0]);
}
@@ -287,7 +287,7 @@ void KyraEngine_MR::enterNewSceneUnk1(int facing, int unk1, int unk2) {
}
void KyraEngine_MR::enterNewSceneUnk2(int unk1) {
- _unk3 = -1;
+ _savedMouseState = -1;
if (_mainCharX == -1 && _mainCharY == -1 && !unk1) {
_mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
updateCharacterAnim(0);
@@ -300,7 +300,7 @@ void KyraEngine_MR::enterNewSceneUnk2(int unk1) {
}
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
}
void KyraEngine_MR::unloadScene() {
@@ -556,8 +556,8 @@ void KyraEngine_MR::initSceneAnims(int unk1) {
for (int i = 0; i < 50; ++i) {
obj = &_animObjects[i+17];
- const Item &item = _itemList[i];
- if (item.id != 0xFFFF && item.sceneId == _mainCharacter.sceneId) {
+ const ItemDefinition &item = _itemList[i];
+ if (item.id != kItemNone && item.sceneId == _mainCharacter.sceneId) {
obj->xPos1 = item.x;
obj->yPos1 = item.y;
animSetupPaletteEntry(obj);
@@ -696,16 +696,16 @@ int KyraEngine_MR::checkSceneChange() {
int facing = 0;
int process = 0;
- if (_screen->getLayer(charX, charY) == 1 && _unk3 == -7) {
+ if (_screen->getLayer(charX, charY) == 1 && _savedMouseState == -7) {
facing = 0;
process = 1;
- } else if (charX >= 316 && _unk3 == -6) {
+ } else if (charX >= 316 && _savedMouseState == -6) {
facing = 2;
process = 1;
- } else if (charY >= 186 && _unk3 == -5) {
+ } else if (charY >= 186 && _savedMouseState == -5) {
facing = 4;
process = 1;
- } else if (charX <= 4 && _unk3 == -4) {
+ } else if (charX <= 4 && _savedMouseState == -4) {
facing = 6;
process = 1;
}
@@ -742,7 +742,7 @@ int KyraEngine_MR::checkSceneChange() {
return 1;
}
int KyraEngine_MR::runSceneScript1(int x, int y) {
- if (y > 187 && _unk3 > -4)
+ if (y > 187 && _savedMouseState > -4)
return 0;
if (_deathHandler >= 0)
return 0;
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index e09cc8123c..8b0a87f0c8 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -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;
}
@@ -3313,7 +3313,7 @@ SJISFont::SJISFont(Screen *s, Graphics::FontSJIS *font, const uint8 invisColor,
: _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _screen(s) {
assert(_font);
- _font->enableOutline(outlineSize);
+ _font->setDrawingMode(outlineSize ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
_sjisWidth = _font->getMaxFontWidth() >> 1;
_fontHeight = _font->getFontHeight() >> 1;
@@ -3345,9 +3345,9 @@ void SJISFont::setColorMap(const uint8 *src) {
if (!_is16Color) {
if (_colorMap[0] == _invisColor)
- _font->enableOutline(false);
+ _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
else
- _font->enableOutline(true);
+ _font->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
}
}
diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h
index 654b7da6b7..e35b9b37b2 100644
--- a/engines/kyra/screen.h
+++ b/engines/kyra/screen.h
@@ -423,6 +423,7 @@ public:
virtual void setScreenDim(int dim) = 0;
virtual const ScreenDim *getScreenDim(int dim) = 0;
+ virtual int screenDimTableCount() const = 0;
const ScreenDim *_curDim;
diff --git a/engines/kyra/screen_hof.h b/engines/kyra/screen_hof.h
index 1c17a424b3..5117716ac0 100644
--- a/engines/kyra/screen_hof.h
+++ b/engines/kyra/screen_hof.h
@@ -39,6 +39,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
// sequence player
void generateGrayOverlay(const Palette &pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool flag);
diff --git a/engines/kyra/screen_lok.h b/engines/kyra/screen_lok.h
index 2a5ef7e12e..0d30c35bfd 100644
--- a/engines/kyra/screen_lok.h
+++ b/engines/kyra/screen_lok.h
@@ -43,6 +43,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
void setTextColorMap(const uint8 *cmap);
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_lol.h b/engines/kyra/screen_lol.h
index 52e66df1ec..9f4d751d0c 100644
--- a/engines/kyra/screen_lol.h
+++ b/engines/kyra/screen_lol.h
@@ -43,8 +43,9 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
- int curDimIndex() { return _curDimIndex; }
+ int curDimIndex() const { return _curDimIndex; }
void modifyScreenDim(int dim, int x, int y, int w, int h);
+ int screenDimTableCount() const { return _screenDimTableCount; }
void fprintString(const char *format, int x, int y, uint8 col1, uint8 col2, uint16 flags, ...) GCC_PRINTF(2, 8);
void fprintStringIntro(const char *format, int x, int y, uint8 c1, uint8 c2, uint8 c3, uint16 flags, ...) GCC_PRINTF(2, 9);
diff --git a/engines/kyra/screen_mr.h b/engines/kyra/screen_mr.h
index 4107003b12..c02fc4bfb2 100644
--- a/engines/kyra/screen_mr.h
+++ b/engines/kyra/screen_mr.h
@@ -39,6 +39,7 @@ public:
void setScreenDim(int dim);
const ScreenDim *getScreenDim(int dim);
+ int screenDimTableCount() const { return _screenDimTableCount; }
int getLayer(int x, int y);
diff --git a/engines/kyra/screen_v2.cpp b/engines/kyra/screen_v2.cpp
index 216bf07f6f..9c224d1562 100644
--- a/engines/kyra/screen_v2.cpp
+++ b/engines/kyra/screen_v2.cpp
@@ -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;
@@ -187,16 +187,22 @@ uint8 *Screen_v2::getPtrToShape(uint8 *shpFile, int shape) {
}
int Screen_v2::getShapeScaledWidth(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
int width = READ_LE_UINT16(shpFile+3);
return (width * scale) >> 8;
}
int Screen_v2::getShapeScaledHeight(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
int height = shpFile[2];
return (height * scale) >> 8;
}
uint16 Screen_v2::getShapeSize(const uint8 *shp) {
+ if (!shp)
+ return 0;
return READ_LE_UINT16(shp+6);
}
diff --git a/engines/kyra/script_hof.cpp b/engines/kyra/script_hof.cpp
index f30f5787e7..d57bb7efc5 100644
--- a/engines/kyra/script_hof.cpp
+++ b/engines/kyra/script_hof.cpp
@@ -410,7 +410,7 @@ int KyraEngine_HoF::o2_countItemsInScene(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemsInScene(%p) (%d)", (const void *)script, stackPos(0));
int count = 0;
for (int i = 0; i < 30; ++i) {
- if (_itemList[i].sceneId == stackPos(0) && _itemList[i].id != 0xFFFF)
+ if (_itemList[i].sceneId == stackPos(0) && _itemList[i].id != kItemNone)
++count;
}
return count;
@@ -1039,7 +1039,7 @@ int KyraEngine_HoF::o2_setColorCodeValue(EMCState *script) {
int KyraEngine_HoF::o2_countItemInstances(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_countItemInstances(%p) (%d)", (const void *)script, stackPos(0));
- uint16 item = stackPos(0);
+ Item item = stackPos(0);
int count = 0;
for (int i = 0; i < 20; ++i) {
@@ -1047,7 +1047,7 @@ int KyraEngine_HoF::o2_countItemInstances(EMCState *script) {
++count;
}
- if (_itemInHand == int16(item))
+ if (_itemInHand == item)
++count;
for (int i = 0; i < 30; ++i) {
@@ -1075,7 +1075,7 @@ int KyraEngine_HoF::o2_removeItemFromScene(EMCState *script) {
const uint16 item = stackPos(1);
for (int i = 0; i < 30; ++i) {
if (_itemList[i].sceneId == scene && _itemList[i].id == item)
- _itemList[i].id = 0xFFFF;
+ _itemList[i].id = kItemNone;
}
return 0;
}
diff --git a/engines/kyra/script_lok.cpp b/engines/kyra/script_lok.cpp
index 1b4a11f793..a2bad8035e 100644
--- a/engines/kyra/script_lok.cpp
+++ b/engines/kyra/script_lok.cpp
@@ -37,6 +37,7 @@
#include "kyra/sound.h"
namespace Kyra {
+
int KyraEngine_LoK::o1_magicInMouseItem(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_magicInMouseItem(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1));
magicInMouseItem(stackPos(0), stackPos(1), -1);
@@ -203,9 +204,7 @@ int KyraEngine_LoK::o1_getElapsedSeconds(EMCState *script) {
int KyraEngine_LoK::o1_mouseIsPointer(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_LoK::o1_mouseIsPointer(%p) ()", (const void *)script);
- if (_itemInHand == -1)
- return 1;
- return 0;
+ return (_itemInHand == kItemNone);
}
int KyraEngine_LoK::o1_runSceneAnimUntilDone(EMCState *script) {
diff --git a/engines/kyra/script_lol.cpp b/engines/kyra/script_lol.cpp
index 1034e86d4e..33fa16a22c 100644
--- a/engines/kyra/script_lol.cpp
+++ b/engines/kyra/script_lol.cpp
@@ -267,6 +267,12 @@ int LoLEngine::olol_setItemProperty(EMCState *script) {
tmp->nameStringId = stackPos(1);
tmp->shpIndex = stackPos(2);
tmp->type = stackPos(3);
+
+ // WORKAROUND for unpatched early floppy versions.
+ // The Vaelan's cube should not be able to be equipped in a weapon slot.
+ if (stackPos(0) == 264 && tmp->type == 5)
+ tmp->type = 0;
+
tmp->itemScriptFunc = stackPos(4);
tmp->might = stackPos(5);
tmp->skill = stackPos(6);
@@ -1065,6 +1071,7 @@ int LoLEngine::olol_createHandItem(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_createHandItem(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2));
if (_itemInHand)
return 0;
+
setHandItem(makeItem(stackPos(0), stackPos(1), stackPos(2)));
return 1;
}
diff --git a/engines/kyra/script_mr.cpp b/engines/kyra/script_mr.cpp
index ae125b5b99..a9637a6378 100644
--- a/engines/kyra/script_mr.cpp
+++ b/engines/kyra/script_mr.cpp
@@ -216,7 +216,7 @@ int KyraEngine_MR::o3_removeInventoryItemInstances(EMCState *script) {
const int item = stackPos(0);
for (int i = 0; i < 10; ++i) {
if (_mainCharacter.inventory[i] == item)
- _mainCharacter.inventory[i] = 0xFFFF;
+ _mainCharacter.inventory[i] = kItemNone;
}
return 0;
}
@@ -293,7 +293,7 @@ int KyraEngine_MR::o3_updateScore(EMCState *script) {
int KyraEngine_MR::o3_makeSecondChanceSave(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_makeSecondChanceSave(%p) ()", (const void *)script);
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
return 0;
}
@@ -594,7 +594,7 @@ int KyraEngine_MR::o3_updateConversations(EMCState *script) {
int KyraEngine_MR::o3_removeItemSlot(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_MR::o3_removeItemSlot(%p) (%d)", (const void *)script, stackPos(0));
deleteItemAnimEntry(stackPos(0));
- _itemList[stackPos(0)].id = 0xFFFF;
+ _itemList[stackPos(0)].id = kItemNone;
return 1;
}
@@ -642,7 +642,7 @@ int KyraEngine_MR::o3_removeItemInstances(EMCState *script) {
for (int i = 0; i < 10; ++i) {
if (_mainCharacter.inventory[i] == item) {
- _mainCharacter.inventory[i] = 0xFFFF;
+ _mainCharacter.inventory[i] = kItemNone;
++deleted;
}
}
@@ -654,7 +654,7 @@ int KyraEngine_MR::o3_removeItemInstances(EMCState *script) {
for (int i = 0; i < 50; ++i) {
if (_itemList[i].id == item) {
- _itemList[i].id = 0xFFFF;
+ _itemList[i].id = kItemNone;
++deleted;
}
}
diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp
index 907c5ee7bd..30291c67db 100644
--- a/engines/kyra/script_tim.cpp
+++ b/engines/kyra/script_tim.cpp
@@ -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();
}
}
}
@@ -946,7 +946,7 @@ int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, i
}
}
- if (wsaFlags & 7)
+ if (wsa && (wsaFlags & 7))
wsa->displayFrame(0, 0, x, y, 0, 0, 0);
if (wsaFlags & 3) {
diff --git a/engines/kyra/script_v2.cpp b/engines/kyra/script_v2.cpp
index 01f058c383..17e882398e 100644
--- a/engines/kyra/script_v2.cpp
+++ b/engines/kyra/script_v2.cpp
@@ -69,7 +69,7 @@ int KyraEngine_v2::o2_trySceneChange(EMCState *script) {
if (success) {
_emc->init(script, script->dataPtr);
_unk4 = 0;
- _unk3 = -1;
+ _savedMouseState = -1;
_unk5 = 1;
return 0;
} else {
diff --git a/engines/kyra/sequences_hof.cpp b/engines/kyra/sequences_hof.cpp
index 0a3e13bb26..e92866490b 100644
--- a/engines/kyra/sequences_hof.cpp
+++ b/engines/kyra/sequences_hof.cpp
@@ -2002,7 +2002,7 @@ void KyraEngine_HoF::seq_processText() {
outputStr[linePos] = *srcStr;
srcStr++;
}
- outputStr[linePos] = 0;
+ outputStr[linePos] = 0;
if (*srcStr == 0x0d)
srcStr++;
@@ -2130,7 +2130,7 @@ void KyraEngine_HoF::seq_cmpFadeFrame(const char *cmpFile) {
_screen->cmpFadeFrameStep(4, 320, 200, 0, 0, 2, 320, 200, 0, 0, 320, 200, 6);
_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0);
_screen->updateScreen();
- delayUntil(endtime);
+ delayUntil(endtime);
}
_screen->copyPage(4, 0);
diff --git a/engines/kyra/sequences_lol.cpp b/engines/kyra/sequences_lol.cpp
index dc1a0f4d15..e8ea7a9dcb 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()) {
@@ -1424,7 +1435,7 @@ void LoLEngine::processCredits(char *t, int dimState, int page, int delayTime) {
if (monsterAnimFrame >= 8)
_screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1);
- _screen->drawShape(page, monsterShape, monsterX, monsterY, 0, 0x104 | ((!isRightMonster | (monsterAnimFrame < 20)) ? 0 : 1), _outroShapeTable, 1, _outroMonsterScaleTableX[monsterAnimFrame], _outroMonsterScaleTableY[monsterAnimFrame]);
+ _screen->drawShape(page, monsterShape, monsterX, monsterY, 0, 0x104 | ((!isRightMonster || monsterAnimFrame < 20) ? 0 : 1), _outroShapeTable, 1, _outroMonsterScaleTableX[monsterAnimFrame], _outroMonsterScaleTableY[monsterAnimFrame]);
if (monsterAnimFrame < 8)
_screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1);
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
index 3b0fbbed0a..8b83796d46 100644
--- a/engines/kyra/sound.h
+++ b/engines/kyra/sound.h
@@ -94,7 +94,7 @@ public:
* @param track track number
* @return true if available, false otherwise
*/
- virtual bool hasSoundFile(uint file) { return (fileListEntry(file) != 0); }
+ virtual bool hasSoundFile(uint file) const { return (fileListEntry(file) != 0); }
/**
* Load a specifc sound file for use of
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 8458d751de..a45972ece7 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -2304,7 +2304,7 @@ void SoundAdLibPC::haltTrack() {
//_vm->_system->delayMillis(3 * 60);
}
-bool SoundAdLibPC::isPlaying() {
+bool SoundAdLibPC::isPlaying() const {
return _driver->callback(7, int(0)) != 0;
}
diff --git a/engines/kyra/sound_adlib.h b/engines/kyra/sound_adlib.h
index 0607e1dd7a..aaca1b9138 100644
--- a/engines/kyra/sound_adlib.h
+++ b/engines/kyra/sound_adlib.h
@@ -76,7 +76,7 @@ public:
void playTrack(uint8 track);
void haltTrack();
- bool isPlaying();
+ bool isPlaying() const;
void playSoundEffect(uint8 track);
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_intern.h b/engines/kyra/sound_intern.h
index f8738bc791..bc1c2be1e2 100644
--- a/engines/kyra/sound_intern.h
+++ b/engines/kyra/sound_intern.h
@@ -68,7 +68,7 @@ public:
void playTrack(uint8 track);
void haltTrack();
- bool isPlaying();
+ bool isPlaying() const;
void playSoundEffect(uint8 track);
void stopAllSoundEffects();
diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp
index 57da51ab5a..8982c1cca4 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) {
@@ -679,7 +683,7 @@ void SoundMidiPC::haltTrack() {
_output->deinitSource(0);
}
-bool SoundMidiPC::isPlaying() {
+bool SoundMidiPC::isPlaying() const {
Common::StackLock lock(_mutex);
return _music->isPlaying();
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 16004159ab..a674f9efb8 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -83,11 +83,11 @@ void SoundTowns::playTrack(uint8 track) {
return;
track -= 2;
- const int32 *const tTable = (const int32 *const)cdaData();
+ const int32 * const tTable = (const int32 *)cdaData();
int tTableIndex = 3 * track;
- int trackNum = (int) READ_LE_UINT32(&tTable[tTableIndex + 2]);
- int32 loop = (int32) READ_LE_UINT32(&tTable[tTableIndex + 1]);
+ int trackNum = (int)READ_LE_UINT32(&tTable[tTableIndex + 2]);
+ int32 loop = (int32)READ_LE_UINT32(&tTable[tTableIndex + 1]);
if (track == _lastTrack && _musicEnabled)
return;
@@ -530,7 +530,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {
if (track == _lastTrack && _musicEnabled)
return;
- const uint16 *const cdaTracks = (const uint16 *const) cdaData();
+ const uint16 * const cdaTracks = (const uint16 *)cdaData();
int trackNum = -1;
if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
@@ -587,7 +587,7 @@ void SoundTownsPC98_v2::beginFadeOut() {
}
int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8, bool) {
- //static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
+ static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
static const char patternHOF[] = "%s.PCM";
static const char patternLOL[] = "%s.VOC";
@@ -608,7 +608,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;
@@ -648,8 +648,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/sprites_lol.cpp b/engines/kyra/sprites_lol.cpp
index 3b63618d6d..6245ecdd1f 100644
--- a/engines/kyra/sprites_lol.cpp
+++ b/engines/kyra/sprites_lol.cpp
@@ -350,7 +350,7 @@ void LoLEngine::monsterDropItems(MonsterInPlay *monster) {
}
}
-void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, int id) {
+void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id) {
uint16 *blockItemIndex = &l->assignedObjects;
ItemInPlay *i = 0;
@@ -367,7 +367,7 @@ void LoLEngine::removeAssignedObjectFromBlock(LevelBlockProperty *l, int id) {
}
}
-void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, int id) {
+void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id) {
uint16 *blockItemIndex = &l->drawObjects;
ItemInPlay *i = 0;
@@ -384,7 +384,7 @@ void LoLEngine::removeDrawObjectFromBlock(LevelBlockProperty *l, int id) {
}
}
-void LoLEngine::assignMonsterToBlock(uint16 *assignedBlockObjects, int id) {
+void LoLEngine::assignMonsterToBlock(uint16 *assignedBlockObjects, uint16 id) {
ItemInPlay *t = findObject(id);
t->nextAssignedObject = *assignedBlockObjects;
*assignedBlockObjects = id;
@@ -901,6 +901,9 @@ void LoLEngine::calcSpriteRelPosition(uint16 x1, uint16 y1, int &x2, int &y2, ui
}
void LoLEngine::drawDoor(uint8 *shape, uint8 *doorPalette, int index, int unk2, int w, int h, int flags) {
+ if (!shape)
+ return;
+
uint8 c = _dscDoor1[(_currentDirection << 5) + unk2];
int r = (c / 5) + 5 * _dscDimMap[index];
uint16 d = _dscShapeOvlIndex[r];
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index 4b71b1d69d..edfa61e5b4 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -42,7 +42,7 @@
namespace Kyra {
-#define RESFILE_VERSION 72
+#define RESFILE_VERSION 73
namespace {
bool checkKyraDat(Common::SeekableReadStream *file) {
@@ -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++)
@@ -912,16 +912,7 @@ void KyraEngine_LoK::loadItems() {
_shapes[216 + i] = _screen->encodeShape( (i % 20) * 16, i/20 * 16, 16, 16, 0);
}
- uint32 size;
- uint8 *fileData = _res->fileData("_ITEM_HT.DAT", &size);
- assert(fileData);
-
- for (int i = 0; i < 107; i++) {
- _itemTable[i].height = fileData[i];
- _itemTable[i].unk1 = _itemTable[i].unk2 = 0;
- }
-
- delete[] fileData;
+ _res->loadFileToBuf("_ITEM_HT.DAT", &_itemHtDat, sizeof(_itemHtDat));
}
void KyraEngine_LoK::loadButtonShapes() {
@@ -1866,9 +1857,9 @@ const uint8 KyraEngine_HoF::_cauldronStateTable[] = {
3, 3, 3, 3, 3, 3, 3
};
-const int16 KyraEngine_HoF::_flaskTable[] = {
+const Item KyraEngine_HoF::_flaskTable[] = {
0x19, 0x14, 0x15, 0x16, 0x17, 0x18, 0x34,
- 0x1B, 0x39, 0x1A, 0x3A, 0x4D, 0x72, -1
+ 0x1B, 0x39, 0x1A, 0x3A, 0x4D, 0x72, kItemNone
};
const uint8 KyraEngine_HoF::_rainbowRoomData[] = {
@@ -1964,10 +1955,10 @@ const uint8 KyraEngine_MR::_inventoryY[] = {
0xB2, 0xB2, 0xB2, 0xB2, 0xB2
};
-const uint8 KyraEngine_MR::_trashItemList[] = {
+const Item KyraEngine_MR::_trashItemList[] = {
0x1E, 0x1D, 0x1C, 0x1F, 0x0F, 0x05, 0x04, 0x00,
0x03, 0x22, 0x0B, 0x20, 0x21, 0x10, 0x11, 0x3A,
- 0x39, 0x40, 0x3E, 0x3D, 0x3C, 0x3F, 0xFF
+ 0x39, 0x40, 0x3E, 0x3D, 0x3C, 0x3F, kItemNone
};
const uint8 KyraEngine_MR::_itemStringPickUp[] = {
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 7f9531507c..c5341cd8a6 100644
--- a/engines/kyra/text_lol.cpp
+++ b/engines/kyra/text_lol.cpp
@@ -46,7 +46,9 @@ TextDisplayer_LoL::TextDisplayer_LoL(LoLEngine *vm, Screen_LoL *screen) : _vm(vm
_currentLine = new char[85];
memset(_currentLine, 0, 85);
- for (int i = 0; i < 14; i++){
+ _textDimData = new TextDimData[_screen->screenDimTableCount()];
+
+ for (int i = 0; i < _screen->screenDimTableCount(); i++){
const ScreenDim *d = _screen->getScreenDim(i);
_textDimData[i].color1 = d->unk8;
_textDimData[i].color2 = d->unkA;
@@ -59,6 +61,7 @@ TextDisplayer_LoL::~TextDisplayer_LoL() {
delete[] _buffer;
delete[] _dialogueBuffer;
delete[] _currentLine;
+ delete[] _textDimData;
}
void TextDisplayer_LoL::setupField(bool mode) {
diff --git a/engines/kyra/text_lol.h b/engines/kyra/text_lol.h
index 488be17cff..1e5bc8884e 100644
--- a/engines/kyra/text_lol.h
+++ b/engines/kyra/text_lol.h
@@ -90,7 +90,7 @@ private:
uint8 line;
};
- TextDimData _textDimData[14];
+ TextDimData *_textDimData;
};
} // End of namespace Kyra
diff --git a/engines/kyra/timer_mr.cpp b/engines/kyra/timer_mr.cpp
index 48fa55519c..0d89decf5a 100644
--- a/engines/kyra/timer_mr.cpp
+++ b/engines/kyra/timer_mr.cpp
@@ -60,7 +60,7 @@ void KyraEngine_MR::timerRunSceneScript7(int arg) {
void KyraEngine_MR::timerFleaDeath(int arg) {
_timer->setCountdown(4, 5400);
- saveGameState(999, "Autosave", 0);
+ saveGameStateIntern(999, "Autosave", 0);
_screen->hideMouse();
_timer->disable(4);
runAnimationScript("FLEADTH1.EMC", 0, 0, 1, 1);
diff --git a/engines/lastexpress/data/animation.cpp b/engines/lastexpress/data/animation.cpp
new file mode 100644
index 0000000000..88973c4b0b
--- /dev/null
+++ b/engines/lastexpress/data/animation.cpp
@@ -0,0 +1,300 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/animation.h"
+
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/events.h"
+#include "engines/engine.h"
+
+namespace LastExpress {
+
+Animation::Animation() : _stream(NULL), _currentChunk(NULL), _overlay(NULL), _background1(NULL), _background2(NULL), _backgroundCurrent(0), _audio(NULL), _startTime(0), _changed(false), _flag(0) {
+}
+
+Animation::~Animation() {
+ reset();
+}
+
+void Animation::reset() {
+ delete _overlay;
+ _overlay = NULL;
+ delete _background1;
+ _background1 = NULL;
+ delete _background2;
+ _background2 = NULL;
+ delete _audio;
+ _audio = NULL;
+
+ _backgroundCurrent = 0;
+ _chunks.clear();
+
+ _currentChunk = NULL;
+
+ delete _stream;
+}
+
+bool Animation::load(Common::SeekableReadStream *stream, int flag) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Keep stream for later decoding of animation
+ _stream = stream;
+
+ // Read header to get the number of chunks
+ uint32 numChunks = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of chunks in NIS file: %d", numChunks);
+
+ // Check if there is enough data
+ if (_stream->size() - _stream->pos() < (signed)(numChunks * sizeof(Chunk))) {
+ debugC(2, kLastExpressDebugGraphics, "NIS file seems to be corrupted!");
+ return false;
+ }
+
+ // Read all the chunks
+ for (uint32 i = 0; i < numChunks; ++i) {
+ Chunk chunk;
+ chunk.type = (ChunkType)_stream->readUint16LE();
+ chunk.frame = _stream->readUint16LE();
+ chunk.size = _stream->readUint32LE();
+
+ _chunks.push_back(chunk);
+
+ debugC(9, kLastExpressDebugGraphics, "Chunk Entry: type 0x%.4x, frame=%d, size=%d", chunk.type, chunk.frame, chunk.size);
+ }
+ _currentChunk = _chunks.begin();
+ _changed = false;
+ _startTime = g_engine->_system->getMillis();
+
+ return true;
+}
+
+bool Animation::process() {
+ if (!_currentChunk)
+ error("Animation::process - internal error: the current chunk iterator is invalid!");
+
+ if (_stream == NULL || _chunks.size() == 0)
+ error("Trying to show an animation before loading data");
+
+ // TODO: substract the time paused by the GUI
+ uint32 currentFrame = (uint32)(((float)(g_engine->_system->getMillis() - _startTime)) / 33.33f);
+
+ // Process all chunks until the current frame
+ while (!_changed && currentFrame > _currentChunk->frame && !hasEnded()) {
+ switch(_currentChunk->type) {
+ //TODO: some info chunks are probably subtitle/sync related
+ case kChunkTypeUnknown1:
+ case kChunkTypeUnknown2:
+ case kChunkTypeUnknown5:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info chunk: type 0x%.4x (size %d)", _currentChunk->type, _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: _currentChunk->size?
+ break;
+
+ case kChunkTypeAudioInfo:
+ debugC(9, kLastExpressDebugGraphics, " audio info: %d blocks", _currentChunk->size);
+ assert (_currentChunk->frame == 0);
+ //TODO: save the size?
+ _audio = new AppendableSound();
+ break;
+
+ case kChunkTypeUnknown4:
+ debugC(9, kLastExpressDebugGraphics | kLastExpressDebugUnknown, " info block 4");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ //TODO unknown type of chunk
+ break;
+
+ case kChunkTypeBackground1:
+ debugC(9, kLastExpressDebugGraphics, " background frame 1 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background1;
+ _background1 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground1:
+ debugC(9, kLastExpressDebugGraphics, " select background 1");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 1;
+ break;
+
+ case kChunkTypeBackground2:
+ debugC(9, kLastExpressDebugGraphics, " background frame 2 (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _background2;
+ _background2 = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeSelectBackground2:
+ debugC(9, kLastExpressDebugGraphics, " select background 2");
+ assert (_currentChunk->frame == 0 && _currentChunk->size == 0);
+ _backgroundCurrent = 2;
+ break;
+
+ case kChunkTypeOverlay:
+ debugC(9, kLastExpressDebugGraphics, " overlay frame (%d bytes, frame %d)", _currentChunk->size, _currentChunk->frame);
+ delete _overlay;
+ _overlay = processChunkFrame(_stream, *_currentChunk);
+ break;
+
+ case kChunkTypeUpdate:
+ case kChunkTypeUpdateTransition:
+ debugC(9, kLastExpressDebugGraphics, " update%s: frame %d", _currentChunk->type == 15 ? "" : " with transition", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _changed = true;
+ break;
+
+ case kChunkTypeAudioData:
+ debugC(9, kLastExpressDebugGraphics, " audio (%d blocks, %d bytes, frame %d)", _currentChunk->size / _soundBlockSize, _currentChunk->size, _currentChunk->frame);
+ processChunkAudio(_stream, *_currentChunk);
+
+ // Synchronize the audio by resetting the start time
+ if (_currentChunk->frame == 0)
+ _startTime = g_engine->_system->getMillis();
+ break;
+
+ case kChunkTypeAudioEnd:
+ debugC(9, kLastExpressDebugGraphics, " audio end: %d blocks", _currentChunk->frame);
+ assert (_currentChunk->size == 0);
+ _audio->finish();
+ //TODO: we need to start the linked sound (.LNK) after the audio from the animation ends
+ break;
+
+ default:
+ error(" UNKNOWN chunk type=%x frame=%d size=%d", _currentChunk->type, _currentChunk->frame, _currentChunk->size);
+ break;
+ }
+ _currentChunk++;
+ }
+
+ return true;
+}
+
+bool Animation::hasEnded() {
+ return _currentChunk == _chunks.end();
+}
+
+Common::Rect Animation::draw(Graphics::Surface *surface) {
+ if (!_overlay)
+ error("Animation::draw - internal error: the current overlay animation frame is invalid!");
+
+ // Paint the background
+ if (_backgroundCurrent == 1 && _background1)
+ _background1->draw(surface);
+ else if (_backgroundCurrent == 2 && _background2)
+ _background2->draw(surface);
+
+ // Paint the overlay
+ _overlay->draw(surface);
+
+ //TODO
+ return Common::Rect();
+}
+
+AnimFrame *Animation::processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const {
+ assert (c.frame == 0);
+
+ // Create a temporary chunk buffer
+ Common::MemoryReadStream *str = in->readStream(c.size);
+
+ // Read the frame information
+ FrameInfo i;
+ i.read(str, false);
+
+ // Decode the frame
+ AnimFrame *f = new AnimFrame(str, i);
+
+ // Delete the temporary chunk buffer
+ delete str;
+
+ return f;
+}
+
+void Animation::processChunkAudio(Common::SeekableReadStream *in, const Chunk &c) {
+ if (!_audio)
+ error("Animation::processChunkAudio - internal error: the audio stream is invalid!");
+
+ // Skip the Snd header, to queue just the audio blocks
+ uint32 size = c.size;
+ if ((c.size % 739) != 0) {
+ // Read Snd header
+ uint32 header1 = in->readUint32LE();
+ uint16 header2 = in->readUint16LE();
+ warning("Start ADPCM: %d, %d", header1, header2);
+ size -= 6;
+ }
+
+ // Append the current chunk to the Snd
+ _audio->queueBuffer(in->readStream(size));
+}
+
+// TODO: this method will probably go away and be integrated in the main loop
+void Animation::play() {
+ while (!hasEnded() && !g_engine->getEventManager()->shouldQuit() && !g_engine->getEventManager()->shouldRTL()) {
+ process();
+
+ if (_changed) {
+ // Create a temporary surface to merge the overlay with the background
+ Graphics::Surface *s = new Graphics::Surface;
+ s->create(640, 480, 2);
+
+ draw(s);
+
+ // XXX: Update the screen
+ g_system->copyRectToScreen((byte *)s->pixels, s->pitch, 0, 0, s->w, s->h);
+
+ // Free the temporary surface
+ s->free();
+ delete s;
+
+ _changed = false;
+ }
+
+ g_system->updateScreen();
+
+ //FIXME: implement subtitles
+ g_engine->_system->delayMillis(20);
+
+ // Handle right-click to interrupt animations
+ Common::Event ev;
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ // Stop audio
+ if (_audio)
+ _audio->finish();
+
+ // TODO start LNK file sound?
+ return;
+ }
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/animation.h b/engines/lastexpress/data/animation.h
new file mode 100644
index 0000000000..ca1f7c6fa0
--- /dev/null
+++ b/engines/lastexpress/data/animation.h
@@ -0,0 +1,114 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_ANIMATION_H
+#define LASTEXPRESS_ANIMATION_H
+
+/*
+ Animation format (.NIS)
+
+ uint32 {4} - Number of chunks
+
+ // for each chunk
+ uint16 {2} - Type
+ uint16 {2} - Tag
+ uint32 {4} - Size of chunk
+ byte {x} - Data (for "data" chunks: backgrounds, overlay & audio data)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class AnimFrame;
+class AppendableSound;
+
+class Animation : public Drawable {
+public:
+ enum FlagType {
+ kFlagDefault = 16384,
+ kFlagProcess = 49152
+ };
+
+ Animation();
+ ~Animation();
+
+ bool load(Common::SeekableReadStream *stream, int flag = kFlagDefault);
+ bool process();
+ bool hasEnded();
+ Common::Rect draw(Graphics::Surface *surface);
+ void play();
+
+private:
+ static const uint32 _soundBlockSize = 739;
+
+ // despite their size field, info chunks don't have a payload
+ enum ChunkType {
+ kChunkTypeUnknown1 = 1,
+ kChunkTypeUnknown2 = 2,
+ kChunkTypeAudioInfo = 3,
+ kChunkTypeUnknown4 = 4,
+ kChunkTypeUnknown5 = 5,
+ kChunkTypeBackground1 = 10,
+ kChunkTypeSelectBackground1 = 11,
+ kChunkTypeBackground2 = 12,
+ kChunkTypeSelectBackground2 = 13,
+ kChunkTypeOverlay = 20,
+ kChunkTypeUpdate = 21,
+ kChunkTypeUpdateTransition = 22,
+ kChunkTypeSound1 = 30,
+ kChunkTypeSound2 = 31,
+ kChunkTypeAudioData = 32,
+ kChunkTypeAudioEnd = 99
+ };
+
+ struct Chunk {
+ ChunkType type;
+ uint16 frame;
+ uint32 size;
+ };
+
+ void reset();
+ AnimFrame *processChunkFrame(Common::SeekableReadStream *in, const Chunk &c) const;
+ void processChunkAudio(Common::SeekableReadStream *in, const Chunk &c);
+
+ Common::SeekableReadStream *_stream;
+ Common::Array<Chunk> _chunks;
+ Common::Array<Chunk>::iterator _currentChunk;
+ AnimFrame *_overlay, *_background1, *_background2;
+ byte _backgroundCurrent;
+ AppendableSound *_audio;
+
+ uint32 _startTime;
+ bool _changed;
+ int _flag;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANIMATION_H
diff --git a/engines/lastexpress/data/archive.cpp b/engines/lastexpress/data/archive.cpp
new file mode 100644
index 0000000000..1a5b6905a3
--- /dev/null
+++ b/engines/lastexpress/data/archive.cpp
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/archive.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace LastExpress {
+
+HPFArchive::HPFArchive(const Common::String &path) {
+ _filename = path;
+
+ // Open a stream to the archive
+ Common::SeekableReadStream *archive = SearchMan.createReadStreamForMember(_filename);
+ if (!archive) {
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", path.c_str());
+ return;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opened archive: %s", path.c_str());
+
+ // Read header to get the number of files
+ uint32 numFiles = archive->readUint32LE();
+ debugC(3, kLastExpressDebugResource, "Number of files in archive: %d", numFiles);
+
+ // Read the list of files
+ for (unsigned int i = 0; i < numFiles; ++i) {
+ char name[13];
+ HPFEntry entry;
+
+ archive->read(&name, sizeof(char) * _archiveNameSize);
+ entry.offset = archive->readUint32LE();
+ entry.size = archive->readUint32LE();
+ entry.isOnHD = archive->readUint16LE();
+
+ // Terminate string
+ name[12] = '\0';
+
+ Common::String filename(name);
+ filename.toLowercase();
+
+ _files[filename] = entry;
+
+ //debugC(9, kLastExpressDebugResource, "File entry: %s (offset:%d - Size: %d - HD: %u)", &name, entry.offset, entry.size, entry.isOnHD);
+ }
+
+ // Close stream
+ delete archive;
+}
+
+bool HPFArchive::hasFile(const Common::String &name) {
+ return (_files.find(name) != _files.end());
+}
+
+int HPFArchive::listMembers(Common::ArchiveMemberList &list) {
+ int numMembers = 0;
+
+ for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(i->_key, this)));
+ numMembers++;
+ }
+
+ return numMembers;
+}
+
+Common::ArchiveMemberPtr HPFArchive::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *HPFArchive::createReadStreamForMember(const Common::String &name) const {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return NULL;
+
+ Common::File *archive = new Common::File();
+ if (!archive->open(_filename)) {
+ delete archive;
+ return NULL;
+ }
+
+ return new Common::SeekableSubReadStream(archive, fDesc->_value.offset * _archiveSectorSize, fDesc->_value.offset * _archiveSectorSize + fDesc->_value.size * _archiveSectorSize, DisposeAfterUse::YES);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/archive.h b/engines/lastexpress/data/archive.h
new file mode 100644
index 0000000000..3860245bc5
--- /dev/null
+++ b/engines/lastexpress/data/archive.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_HPFARCHIVE_H
+#define LASTEXPRESS_HPFARCHIVE_H
+
+/*
+ Archive file format (.HPF)
+
+ uint32 {4} - number of files
+
+ // for each file
+ char {12} - name (zero-terminated)
+ uint32 {4} - offset (expressed in sectors of 2048 bytes)
+ uint32 {4} - size (expressed in sectors of 2048 bytes)
+ byte {2} - file status: 1 = on disk (ie. in HD.HPF), 0 = on CD
+*/
+
+#include "common/archive.h"
+
+namespace LastExpress {
+
+class HPFArchive : public Common::Archive {
+public:
+ HPFArchive(const Common::String &path);
+
+ 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;
+
+ int count() { return _files.size(); }
+
+private:
+ static const unsigned int _archiveNameSize = 12;
+ static const unsigned int _archiveSectorSize = 2048;
+
+ // File entry
+ struct HPFEntry {
+ uint32 offset; ///< Offset (in sectors of 2048 bytes)
+ uint32 size; ///< Size (in sectors of 2048 bytes)
+ uint16 isOnHD; ///< File location (1: on HD; 0: on CD)
+ };
+
+ typedef Common::HashMap<Common::String, HPFEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+
+ FileMap _files; ///< List of files
+ Common::String _filename; ///< Filename of the archive
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HPFARCHIVE_H
diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp
new file mode 100644
index 0000000000..94d7fb16c3
--- /dev/null
+++ b/engines/lastexpress/data/background.cpp
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code and Xentax Wiki documentation
+// http://oezmen.eu/
+// http://wiki.xentax.com/index.php/The_Last_Express_BG
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/debug.h"
+
+namespace LastExpress {
+
+Background::Background() : _data(NULL) {
+ memset(&_header, 0, sizeof(BackgroundHeader));
+}
+
+Background::~Background() {
+ delete[] _data;
+}
+
+bool Background::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ delete[] _data;
+
+ // Load Background header
+ _header.posX = stream->readUint32LE();
+ _header.posY = stream->readUint32LE();
+ _header.width = stream->readUint32LE();
+ _header.height = stream->readUint32LE();
+ _header.redSize = stream->readUint32LE();
+ _header.blueSize = stream->readUint32LE();
+ _header.greenSize = stream->readUint32LE();
+
+ debugC(3, kLastExpressDebugGraphics, "Background Info: (%d, %d) - (%d x %d) - (%d, %d, %d)",
+ _header.posX, _header.posY, _header.width, _header.height,
+ _header.redSize, _header.blueSize, _header.greenSize);
+
+ // Load and decompress Background channel data
+ uint32 numPix = _header.width * _header.height;
+ byte *dataR = decodeComponent(stream, _header.redSize, numPix);
+ byte *dataB = decodeComponent(stream, _header.blueSize, numPix);
+ byte *dataG = decodeComponent(stream, _header.greenSize, numPix);
+
+ // Save to pixel buffer
+ // FIXME handle big-endian case
+ _data = new uint16[_header.width * _header.height];
+ for (uint i = 0; i < _header.width * _header.height; i++)
+ _data[i] = (uint16)((dataR[i] << 10) + (dataG[i] << 5) + dataB[i]);
+
+ // Cleanup buffers
+ delete[] dataR;
+ delete[] dataG;
+ delete[] dataB;
+
+ delete stream;
+
+ return true;
+}
+
+Common::Rect Background::draw(Graphics::Surface *surface) {
+ if (!_data) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to show a background before loading data!");
+ return Common::Rect();
+ }
+
+ int i = 0;
+ for (uint16 y = 0; y < _header.height; y++) {
+ for (uint16 x = 0; x < _header.width; x++) {
+ surface->fillRect(Common::Rect((int16)(_header.posX + x), (int16)(_header.posY + y), (int16)(_header.posX + x + 1), (int16)(_header.posY + y + 1)), _data[i]);
+ i ++;
+ }
+ }
+
+ return Common::Rect((int16)_header.posX, (int16)_header.posY, (int16)(_header.posX + _header.width), (int16)(_header.posY + _header.height));
+}
+
+byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const {
+ // Create the destination array
+ byte *out = new byte[outSize];
+ if (!out)
+ return NULL;
+
+ // Initialize the decoding
+ uint32 inPos = 0;
+ uint32 outPos = 0;
+
+ // Decode
+ while (inPos < inSize) {
+ byte inByte = in->readByte();
+ inPos++;
+
+ if (inByte < 0x80) {
+ // Direct decompression (RLE)
+ byte len = (inByte >> 5) + 1;
+ byte data = inByte & 0x1f;
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = data;
+ } else {
+ // Buffer back reference, 4096 byte window
+ // Take inByte and the following value as a big endian
+ // OfsLen while zeroing the first bit
+ uint16 ofsLen = ((inByte & 0x7F) << 8) | in->readByte();
+ inPos++;
+
+ int32 len = (ofsLen >> 12) + 3;
+ int32 hisPos = (int32)(outPos + (ofsLen & 0x0FFF) - 4096);
+ for (int i = 0; i < len && outPos < outSize; i++)
+ out[outPos++] = out[hisPos++];
+ }
+ }
+
+ return out;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/background.h b/engines/lastexpress/data/background.h
new file mode 100644
index 0000000000..fc1cc26fa4
--- /dev/null
+++ b/engines/lastexpress/data/background.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_BACKGROUND_H
+#define LASTEXPRESS_BACKGROUND_H
+
+/*
+ Background file format (.BG)
+
+ header:
+ uint32 {4} - position X on screen
+ uint32 {4} - position Y on screen
+ uint32 {4} - image width
+ uint32 {4} - image height
+ uint32 {4} - red colour channel data size
+ uint32 {4} - blue colour channel data size
+ uint32 {4} - green colour channel data size
+
+ data:
+ byte {x} - red colour channel data
+ byte {x} - blue colour channel data
+ byte {x} - green colour channel data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Background : public Drawable {
+public:
+ Background();
+ ~Background();
+
+ bool load(Common::SeekableReadStream *stream);
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ struct BackgroundHeader {
+ uint32 posX; ///< position X on screen
+ uint32 posY; ///< position Y on screen
+ uint32 width; ///< image width
+ uint32 height; ///< image height
+ uint32 redSize; ///< red color channel data size
+ uint32 blueSize; ///< blue color channel data size
+ uint32 greenSize; ///< green color channel data size
+ };
+
+ BackgroundHeader _header;
+ uint16 *_data; ///< decoded background data
+
+ byte *decodeComponent(Common::SeekableReadStream *in, uint32 inSize, uint32 outSize) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BACKGROUND_H
diff --git a/engines/lastexpress/data/cursor.cpp b/engines/lastexpress/data/cursor.cpp
new file mode 100644
index 0000000000..4e7003578a
--- /dev/null
+++ b/engines/lastexpress/data/cursor.cpp
@@ -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$
+ *
+ */
+
+#include "lastexpress/data/cursor.h"
+
+#include "lastexpress/lastexpress.h"
+
+#include "common/system.h"
+#include "graphics/cursorman.h"
+
+namespace LastExpress {
+
+Cursor::Cursor() : _current(kCursorMAX) {
+ memset(&_cursors, 0, sizeof(_cursors));
+}
+
+bool Cursor::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Load the whole file to memory
+ Common::MemoryReadStream *data = stream->readStream((uint32) stream->size());
+ delete stream;
+ if (!data)
+ return false;
+
+ // Read the hotspot data
+ for (int i = 0; i < kCursorMAX; i++) {
+ _cursors[i].hotspotX = data->readUint16LE();
+ _cursors[i].hotspotY = data->readUint16LE();
+ debugC(15, kLastExpressDebugCursor | kLastExpressDebugAll,
+ "Cursor %d hotspot x: %d, hotspot y: %d",
+ i, _cursors[i].hotspotX, _cursors[i].hotspotY);
+ }
+
+ // Read the pixel data
+ for (int i = 0; i < kCursorMAX; i++)
+ for (int pix = 0; pix < 32 * 32; pix++)
+ _cursors[i].image[pix] = data->readUint16LE();
+
+ delete data;
+ return true;
+}
+
+void Cursor::show(bool visible) const {
+ CursorMan.showMouse(visible);
+}
+
+bool Cursor::checkStyle(CursorStyle style) const {
+ if (style >= kCursorMAX) {
+ debugC(2, kLastExpressDebugGraphics, "Trying to use an invalid cursor style: was %d, max %d", (int)style, kCursorMAX);
+ return false;
+ }
+
+ return true;
+}
+
+void Cursor::setStyle(CursorStyle style) {
+ if (!checkStyle(style))
+ return;
+
+ if (style == _current)
+ return;
+
+ debugC(10, kLastExpressDebugCursor | kLastExpressDebugAll, "Cursor: setting style: %d", style);
+
+ // Save the new cursor
+ _current = style;
+
+ // Reuse the screen pixel format
+ Graphics::PixelFormat pf = g_system->getScreenFormat();
+ CursorMan.replaceCursor((const byte *)getCursorImage(style),
+ 32, 32, _cursors[style].hotspotX, _cursors[style].hotspotY,
+ 0, 1, &pf);
+}
+
+const uint16 *Cursor::getCursorImage(CursorStyle style) const {
+ if (!checkStyle(style))
+ return NULL;
+
+ return _cursors[style].image;
+}
+
+
+Icon::Icon(CursorStyle style) : _style(style), _x(0), _y(0), _brightness(100) {}
+
+void Icon::setPosition(int16 x, int16 y) {
+ _x = x;
+ _y = y;
+}
+
+void Icon::setBrightness(uint brightness) {
+ assert(brightness <= 100);
+
+ _brightness = (uint8)brightness;
+}
+
+Common::Rect Icon::draw(Graphics::Surface *surface) {
+ const uint16 *image = ((LastExpressEngine *)g_engine)->getCursor()->getCursorImage((CursorStyle)_style);
+ if (!image)
+ return Common::Rect();
+
+ // TODO adjust brightness. The original game seems to be using a table for that (at least in the highlighting case)
+ for (int j = 0; j < 32; j++) {
+ uint16 *s = (uint16 *)surface->getBasePtr(_x, _y + j);
+ for (int i = 0; i < 32; i++) {
+ if (_brightness == 100)
+ *s = *image;
+ else
+ // HACK change color to show highlight
+ *s = (*image & 0x739C) >> 1;
+
+ // Update the image and surface pointers
+ image++;
+ s++;
+ }
+ }
+
+ return Common::Rect(_x, _y, _x + 32, _y + 32);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/cursor.h b/engines/lastexpress/data/cursor.h
new file mode 100644
index 0000000000..992266569f
--- /dev/null
+++ b/engines/lastexpress/data/cursor.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 LASTEXPRESS_CURSOR_H
+#define LASTEXPRESS_CURSOR_H
+
+/*
+ Cursor format (CURSORS.TBM)
+
+ style table:
+ (for each cursor)
+ uint16 {2} - hotspot X
+ uint16 {2} - hotspot Y
+
+ data:
+ (for each cursor)
+ uint16 {32*32} - cursor data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Icon : public Drawable {
+public:
+ Icon(CursorStyle style);
+
+ void setPosition(int16 x, int16 y);
+ void setBrightness(uint brightness);
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ CursorStyle _style;
+ int16 _x, _y;
+ uint8 _brightness;
+};
+
+class Cursor {
+public:
+ Cursor();
+
+ bool load(Common::SeekableReadStream *stream);
+ void show(bool visible) const;
+
+ void setStyle(CursorStyle style);
+ CursorStyle getStyle() const { return _current; }
+
+private:
+ // Style
+ CursorStyle _current;
+
+ // Cursors data
+ struct {
+ uint16 image[32 * 32];
+ uint16 hotspotX, hotspotY;
+ } _cursors[kCursorMAX];
+
+ bool checkStyle(CursorStyle style) const;
+ const uint16 *getCursorImage(CursorStyle style) const;
+
+ // Only allow full access for drawing (needed for getCursorImage)
+ friend Common::Rect Icon::draw(Graphics::Surface *surface);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CURSOR_H
diff --git a/engines/lastexpress/data/font.cpp b/engines/lastexpress/data/font.cpp
new file mode 100644
index 0000000000..5f4b3b40b8
--- /dev/null
+++ b/engines/lastexpress/data/font.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$
+ *
+ */
+
+#include "lastexpress/data/font.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+Font::Font() : _numGlyphs(0), _glyphs(NULL), _glyphWidths(0) {
+ memset(&_palette, 0, sizeof(_palette));
+ memset(&_charMap, 0, sizeof(_charMap));
+}
+
+Font::~Font() {
+ reset();
+}
+
+void Font::reset() {
+ delete[] _glyphs;
+ delete[] _glyphWidths;
+}
+
+bool Font::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ // Read the palette
+ for (uint i = 0; i < _paletteSize; i++) {
+ _palette[i] = stream->readUint16LE();
+ }
+
+ // Read the character map
+ stream->read(_charMap, _charMapSize);
+
+ // Read the glyphs
+ _numGlyphs = stream->readUint16LE();
+ _glyphs = new byte[_numGlyphs * 18 * 8];
+ stream->read(_glyphs, _numGlyphs * 18 * 8);
+
+ // TODO: Read something else?
+ //uint16 unknown = fontFile->readByte();
+ //warning("unknown = %d", unknown);
+ //warning("pos = %d", fontFile->pos());
+ //warning("left = %d", fontFile->size() - fontFile->pos());
+
+ //while (!fontFile->eos()) {
+ //unknown = fontFile->readByte();
+ //warning("val = %d", unknown);
+ //}
+
+ // Precalculate glyph widths
+ _glyphWidths = new byte[_numGlyphs];
+ for (uint16 i = 0; i < _numGlyphs; i++) {
+ _glyphWidths[i] = getGlyphWidth(i);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+
+uint16 Font::getCharGlyph(uint16 c) const {
+ //warning("%c", c);
+ if (c >= 0x200)
+ error("Express::Font: Invalid character %d", c);
+
+ return _charMap[c];
+}
+
+byte *Font::getGlyphImg(uint16 g) {
+ if (!_glyphs)
+ error("Express::getGlyphImg: Invalid glyphs!");
+
+ if (g >= _numGlyphs)
+ error("Express::getGlyphImg: Invalid glyph %d (%d available)", g, _numGlyphs);
+
+ return _glyphs + g * 18 * 8;
+}
+
+uint8 Font::getGlyphWidth(uint16 g) {
+ byte *p = getGlyphImg(g);
+
+ uint8 maxLineWidth = 0;
+ for (int j = 0; j < 18; j++) {
+ uint8 currentLineWidth = 0;
+ for (uint8 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f)
+ currentLineWidth = i;
+ if (i % 2)
+ p++;
+ }
+ if (currentLineWidth > maxLineWidth)
+ maxLineWidth = currentLineWidth;
+ }
+
+ return maxLineWidth;
+}
+
+byte *Font::getCharImg(uint16 c) {
+ return getGlyphImg(getCharGlyph(c));
+}
+
+uint8 Font::getCharWidth(uint16 c) const{
+ if (c == 0x20) {
+ // Space is a special case
+ // TODO: this is an arbitrary value
+ return 10;
+ } else {
+ if (!_glyphWidths)
+ error("Express::getCharWidth: Invalid glyphs widths!");
+
+ return _glyphWidths[getCharGlyph(c)];
+ }
+}
+
+uint16 Font::getStringWidth(Common::String str) const {
+ uint16 width = 0;
+ for (uint i = 0; i < str.size(); i++)
+ width += getCharWidth((unsigned) (int)str[i]);
+
+ return width;
+}
+
+uint16 Font::getStringWidth(const uint16 *str, uint16 length) const {
+ uint16 width = 0;
+ for (uint i = 0; i < length; i++)
+ width += getCharWidth(str[i]);
+
+ return width;
+}
+
+void Font::drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c) {
+ byte *p = getCharImg(c);
+
+ for (int16 j = 0; j < 18; j++) {
+ for (int16 i = 0; i < 16; i++) {
+ byte index;
+ if (i % 2)
+ index = *p & 0xf;
+ else
+ index = *p >> 4;
+ uint16 color = _palette[index];
+ if (color != 0x1f) {
+ surface->fillRect(Common::Rect(x+i, y+j, x+i+1, y+j+1), color);
+ }
+ if (i % 2)
+ p++;
+ }
+ }
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str) {
+ int16 currentX = x;
+ for (uint i = 0; i < str.size(); i++) {
+ drawChar(surface, currentX, y, (unsigned) (int)str[i]);
+ currentX += getCharWidth((unsigned) (int)str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+Common::Rect Font::drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length) {
+ int16 currentX = x;
+ for (uint i = 0; i < length; i++) {
+ drawChar(surface, currentX, y, str[i]);
+ currentX += getCharWidth(str[i]);
+ }
+
+ return Common::Rect(x, y, x + currentX, y + (int16)_charHeight);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/font.h b/engines/lastexpress/data/font.h
new file mode 100644
index 0000000000..457c0c3432
--- /dev/null
+++ b/engines/lastexpress/data/font.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 LASTEXPRESS_FONT_H
+#define LASTEXPRESS_FONT_H
+
+/*
+ Font format (FONT.DAT)
+
+ uint16 {40} - Palette data
+ byte {200} - Character map
+ uint16 {2} - Number of glyphs
+
+ // For each glyph
+ byte {18*8} - Glyph data
+
+ byte {x} - Unknown data (probably just garbage)
+*/
+
+#include "common/stream.h"
+#include "graphics/surface.h"
+
+namespace LastExpress {
+
+class Font {
+public:
+ Font();
+ ~Font();
+
+ bool load(Common::SeekableReadStream *stream);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, Common::String str);
+ Common::Rect drawString(Graphics::Surface *surface, int16 x, int16 y, const uint16 *str, uint16 length);
+
+private:
+ static const uint32 _paletteSize = 0x10;
+ static const uint32 _charMapSize = 0x200;
+ static const uint32 _charHeight = 16;
+
+ void reset();
+
+ uint16 getCharGlyph(uint16 c) const;
+ byte *getGlyphImg(uint16 g);
+ uint8 getGlyphWidth(uint16 g);
+ byte *getCharImg(uint16 c);
+ uint8 getCharWidth(uint16 c) const;
+ uint16 getStringWidth(Common::String str) const;
+ uint16 getStringWidth(const uint16 *str, uint16 length) const;
+ void drawChar(Graphics::Surface *surface, int16 x, int16 y, uint16 c);
+
+ // Font data
+ uint16 _palette[_paletteSize];
+ uint8 _charMap[_charMapSize];
+ uint16 _numGlyphs;
+ byte *_glyphs;
+ uint8 *_glyphWidths;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FONT_H
diff --git a/engines/lastexpress/data/scene.cpp b/engines/lastexpress/data/scene.cpp
new file mode 100644
index 0000000000..4c8cb9bd17
--- /dev/null
+++ b/engines/lastexpress/data/scene.cpp
@@ -0,0 +1,292 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/data/background.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+SceneHotspot *SceneHotspot::load(Common::SeekableReadStream *stream) {
+ SceneHotspot *hs = new SceneHotspot();
+
+ // Rect
+ hs->rect.left = (int16)stream->readUint16LE();
+ hs->rect.right = (int16)stream->readUint16LE();
+ hs->rect.top = (int16)stream->readUint16LE();
+ hs->rect.bottom = (int16)stream->readUint16LE();
+
+ hs->coordsOffset = stream->readUint32LE();
+ hs->scene = (SceneIndex)stream->readUint16LE();
+ hs->location = stream->readByte();
+ hs->action = (Action)stream->readByte();
+ hs->param1 = stream->readByte();
+ hs->param2 = stream->readByte();
+ hs->param3 = stream->readByte();
+ hs->cursor = stream->readByte();
+ hs->next = stream->readUint32LE();
+
+ debugC(10, kLastExpressDebugScenes, "\thotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
+ hs->scene, hs->location, hs->action, hs->param1, hs->param2, hs->param3, hs->cursor, hs->rect.left, hs->rect.top, hs->rect.right, hs->rect.bottom);
+ debugC(10, kLastExpressDebugScenes, "\t coords=%d next=%d ", hs->coordsOffset, hs->next);
+
+ // Read all coords data
+ uint32 offset = hs->coordsOffset;
+ while (offset != 0) {
+
+ SceneCoord *sceneCoord = new SceneCoord;
+
+ stream->seek(offset, SEEK_SET);
+
+ sceneCoord->field_0 = stream->readSint32LE();
+ sceneCoord->field_4 = stream->readSint32LE();
+ sceneCoord->field_8 = stream->readByte();
+ sceneCoord->next = stream->readUint32LE();
+
+ hs->_coords.push_back(sceneCoord);
+
+ offset = sceneCoord->next;
+ }
+
+ return hs;
+}
+
+Common::String SceneHotspot::toString() const {
+ Common::String output = "";
+
+ output += Common::String::printf(" hotspot: scene=%d location=%02d action=%d param1=%02d param2=%02d param3=%02d cursor=%02d rect=(%d, %d)x(%d, %d)",
+ scene, location, action, param1, param2, param3, cursor, rect.left, rect.top, rect.right, rect.bottom);
+
+ return output;
+}
+
+bool SceneHotspot::isInside(const Common::Point &point) {
+
+ bool contains = rect.contains(point);
+
+ if (_coords.empty() || !contains)
+ return contains;
+
+ // Checks extended coordinates
+ for (uint i = 0; i < _coords.size(); i++) {
+
+ SceneCoord *sCoord = _coords[i];
+
+ bool cont;
+ if (sCoord->field_8)
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) >= 0;
+ else
+ cont = (sCoord->field_4 + point.x * sCoord->field_0 + 1000 * point.y) <= 0;
+
+ if (!cont)
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene
+Scene::~Scene() {
+ // Free the hotspots
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ delete _hotspots[i];
+}
+
+Scene *Scene::load(Common::SeekableReadStream *stream) {
+ Scene *scene = new Scene();
+
+ stream->read(&scene->_name, sizeof(scene->_name));
+ scene->_sig = stream->readByte();
+ scene->entityPosition = (EntityPosition)stream->readUint16LE();
+ scene->location = (Location)stream->readUint16LE();
+ scene->car = (CarIndex)stream->readUint16LE();
+ scene->position = stream->readByte();
+ scene->type = (Type)stream->readByte();
+ scene->param1 = stream->readByte();
+ scene->param2 = stream->readByte();
+ scene->param3 = stream->readByte();
+ scene->_hotspot = stream->readUint32LE();
+
+ return scene;
+}
+
+void Scene::loadHotspots(Common::SeekableReadStream *stream) {
+ if (!_hotspots.empty())
+ return;
+
+ debugC(10, kLastExpressDebugScenes, "Scene: name=%s, sig=%02d, entityPosition=%d, location=%d", _name, _sig, entityPosition, location);
+ debugC(10, kLastExpressDebugScenes, "\tcar=%02d, position=%02d, type=%02d, param1=%02d", car, position, type, param1);
+ debugC(10, kLastExpressDebugScenes, "\tparam2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Read all hotspots
+ if (_hotspot != 0) {
+ stream->seek((int32)_hotspot, SEEK_SET);
+ SceneHotspot *hotspot = SceneHotspot::load(stream);
+ while (hotspot) {
+ _hotspots.push_back(hotspot);
+
+ if (hotspot->next == 0)
+ break;
+
+ stream->seek((int32)hotspot->next, SEEK_SET);
+ hotspot = SceneHotspot::load(stream);
+ }
+ }
+}
+
+bool Scene::checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot) {
+ bool found = false;
+ int _location = 0;
+
+ for (int i = 0; i < (int)_hotspots.size(); i++) {
+ if (_hotspots[i]->isInside(coord)) {
+ if (_location <= _hotspots[i]->location) {
+ _location = _hotspots[i]->location;
+ *hotspot = _hotspots[i];
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
+SceneHotspot *Scene::getHotspot(uint index) {
+ if (_hotspots.empty())
+ error("Scene::getHotspot: scene does not have any hotspots!");
+
+ if (index >= _hotspots.size())
+ error("Scene::getHotspot: invalid index (was: %d, max: %d)", index, _hotspots.size() - 1);
+
+ return _hotspots[index];
+}
+
+Common::Rect Scene::draw(Graphics::Surface *surface) {
+ // Safety checks
+ Common::Rect rect;
+
+ Common::String sceneName(_name);
+ sceneName.trim();
+ if (sceneName.empty())
+ error("Scene::draw: This scene is not a valid drawing scene!");
+
+ // Load background
+ Background *background = ((LastExpressEngine *)g_engine)->getResourceManager()->loadBackground(sceneName);
+ if (background) {
+ rect = background->draw(surface);
+ delete background;
+ }
+
+ return rect;
+}
+
+Common::String Scene::toString() {
+ Common::String output = "";
+
+ output += Common::String::printf("Scene: name=%s, sig=%02d, entityPosition=%d, location=%d\n", _name, _sig, entityPosition, location);
+ output += Common::String::printf(" car=%02d, position=%02d, type=%02d, param1=%02d\n", car, position, type, param1);
+ output += Common::String::printf(" param2=%02d, param3=%02d, hotspot=%d\n", param2, param3, _hotspot);
+
+ // Hotspots
+ if (_hotspots.size() != 0) {
+ output += "\nHotspots:\n";
+ for (int i = 0; i < (int)_hotspots.size(); i++)
+ output += _hotspots[i]->toString() + "\n";
+ }
+
+ return output;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SceneLoader
+SceneLoader::SceneLoader() : _stream(NULL) {}
+
+SceneLoader::~SceneLoader() {
+ clear();
+}
+
+void SceneLoader::clear() {
+ // Remove all scenes
+ for (int i = 0; i < (int)_scenes.size(); i++)
+ delete _scenes[i];
+
+ _scenes.clear();
+
+ delete _stream;
+}
+
+bool SceneLoader::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ clear();
+
+ _stream = stream;
+
+ // Read the default scene to get the total number of scenes
+ Scene *header = Scene::load(_stream);
+ if (!header)
+ error("SceneLoader::load: Invalid data file!");
+
+ debugC(2, kLastExpressDebugScenes, " found %d entries", header->entityPosition); /* Header entityPosition is the scene count */
+
+ if (header->entityPosition > 2500) {
+ delete header;
+
+ return false;
+ }
+
+ _scenes.push_back(header);
+
+ // Read all the chunks
+ for (uint i = 0; i < (uint)header->entityPosition; ++i) {
+ Scene *scene = Scene::load(_stream);
+ if (!scene)
+ break;
+
+ _scenes.push_back(scene);
+ }
+
+ return true;
+}
+
+Scene *SceneLoader::get(SceneIndex index) {
+ if (_scenes.empty())
+ return NULL;
+
+ if (index > _scenes.size())
+ return NULL;
+
+ // Load the hotspots if needed
+ _scenes[(int)index]->loadHotspots(_stream);
+
+ return _scenes[(int)index];
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/scene.h b/engines/lastexpress/data/scene.h
new file mode 100644
index 0000000000..c4a34e127b
--- /dev/null
+++ b/engines/lastexpress/data/scene.h
@@ -0,0 +1,245 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SCENE_H
+#define LASTEXPRESS_SCENE_H
+
+/*
+ Scene format (CDTRAIN.DAT)
+
+ (text:00484750)
+ header (24 bytes)
+ char {8} - entry name (null terminated)
+ byte {1} - 0xCD
+ uint16 {2} - number of scenes (for first entry - always 0 after?)
+ uint16 {2} - 11 ??
+ uint16 {2} - car
+ byte {1} - camera position (used to get the proper sequences to show)
+ byte {1} - type
+ byte {1} - param1
+ byte {1} - param2
+ byte {1} - param3
+ uint32 {4} - Offset to hotspot info struct
+
+ probably contains cursor type too / scene index : 0 - 2500 (max)
+
+ hotspot info (24 bytes)
+ uint16 {2} - left
+ uint16 {2} - right
+ uint16 {2} - top
+ uint16 {2} - bottom
+ uint32 {4} - scene coords data
+ uint16 {2} - scene
+ byte {1} - location;
+ byte {1} - action;
+ byte {1} - param1;
+ byte {1} - param2;
+ byte {1} - param3
+ byte {1} - cursor
+ uint32{4} - offset to next hotpost
+
+ coords data (9 bytes)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ byte {1} - ??
+ uint32 {4} - offset to next coords data structure
+
+*/
+
+#include "lastexpress/drawable.h"
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Scene;
+
+class SceneHotspot {
+public:
+ enum Action {
+ kActionInventory = 1,
+ kActionSavePoint = 2,
+ kActionPlaySound = 3,
+ kActionPlayMusic = 4,
+ kActionKnockOnDoor = 5,
+ kActionCompartment = 6,
+ kActionPlaySounds = 7,
+ kActionPlayAnimation = 8,
+ kActionOpenCloseObject = 9,
+ kActionObjectUpdateLocation2 = 10,
+ kActionSetItemLocation = 11,
+ kAction12 = 12,
+ kActionPickItem = 13,
+ kActionDropItem = 14,
+ kAction15 = 15,
+ kActionEnterCompartment = 16,
+ kActionGetOutsideTrain = 18,
+ kActionSlip = 19,
+ kActionGetInsideTrain = 20,
+ kActionClimbUpTrain = 21,
+ kActionClimbDownTrain = 22,
+ kActionJumpUpDownTrain = 23,
+ kActionUnbound = 24,
+ kAction25 = 25,
+ kAction26 = 26,
+ kAction27 = 27,
+ kActionConcertSitCough = 28,
+ kAction29 = 29,
+ kActionCatchBeetle = 30,
+ kActionExitCompartment = 31,
+ kAction32 = 32,
+ KActionUseWhistle = 33,
+ kActionOpenMatchBox = 34,
+ kActionOpenBed = 35,
+ kActionDialog = 37,
+ kActionEggBox = 38,
+ kAction39 = 39,
+ kActionBed = 40,
+ kAction41 = 41,
+ kAction42 = 42,
+ kActionSwitchChapter = 43,
+ kAction44 = 44
+ };
+
+ struct SceneCoord {
+ int32 field_0;
+ int32 field_4;
+ byte field_8;
+ uint32 next;
+
+ SceneCoord() {
+ field_0 = 0;
+ field_4 = 0;
+ field_8 = 0;
+ next = 0;
+ }
+ };
+
+ Common::Rect rect;
+ uint32 coordsOffset;
+ SceneIndex scene;
+ byte location;
+ Action action;
+ byte param1;
+ byte param2;
+ byte param3;
+ byte cursor;
+ uint32 next;
+
+ SceneHotspot() {}
+ static SceneHotspot *load(Common::SeekableReadStream *stream);
+
+ bool isInside(const Common::Point &point);
+
+ Common::String toString() const;
+
+private:
+ Common::Array<SceneCoord *> _coords;
+};
+
+class SceneLoader {
+public:
+ SceneLoader();
+ ~SceneLoader();
+
+ bool load(Common::SeekableReadStream *stream);
+ Scene *get(SceneIndex index);
+
+ uint32 count() const { return _scenes.size() - 1; }
+
+private:
+ Common::SeekableReadStream *_stream;
+ Common::Array<Scene *> _scenes;
+
+ void clear();
+};
+
+class Scene : public Drawable {
+public:
+ // Enumerations
+ enum Type {
+ // PreProcess
+ kTypeObject = 1,
+ kTypeItem = 2,
+ kTypeItem2 = 3,
+ kTypeObjectItem = 4,
+ kTypeItem3 = 5,
+ kTypeObjectLocation2 = 6,
+ kTypeCompartments = 7,
+ kTypeCompartmentsItem = 8,
+
+ // PostProcess
+ kTypeList = 128,
+ kTypeSavePointChapter = 129,
+ kTypeLoadBeetleSequences = 130,
+ kTypeGameOver = 131,
+ kTypeReadText = 132,
+ kType133 = 133
+ };
+
+ // Data
+ EntityPosition entityPosition;
+ Location location;
+ CarIndex car;
+ Position position;
+ Type type;
+ byte param1;
+ byte param2;
+ byte param3;
+
+ ~Scene();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ // Hotspots
+ Common::Array<SceneHotspot *> *getHotspots() { return &_hotspots; }
+ bool checkHotSpot(const Common::Point &coord, SceneHotspot **hotspot);
+ SceneHotspot *getHotspot(uint index = 0);
+
+ Common::String toString();
+
+private:
+ char _name[8];
+ byte _sig;
+ uint32 _hotspot;
+
+ Scene() {}
+ Common::Array<SceneHotspot *> _hotspots;
+
+ static Scene *load(Common::SeekableReadStream *stream);
+ void loadHotspots(Common::SeekableReadStream *stream);
+
+ void clear();
+
+ // Only allow full access for loading the scene and the hotspots
+ friend bool SceneLoader::load(Common::SeekableReadStream *stream);
+ friend Scene *SceneLoader::get(SceneIndex index);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENE_H
diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp
new file mode 100644
index 0000000000..cf71bd6a61
--- /dev/null
+++ b/engines/lastexpress/data/sequence.cpp
@@ -0,0 +1,482 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/debug.h"
+
+namespace LastExpress {
+
+void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) {
+ // Save the current position
+ int32 basePos = in->pos();
+
+ dataOffset = in->readUint32LE();
+ unknown = in->readUint32LE();
+ paletteOffset = in->readUint32LE();
+ xPos1 = in->readUint32LE();
+ yPos1 = in->readUint32LE();
+ xPos2 = in->readUint32LE();
+ yPos2 = in->readUint32LE();
+ initialSkip = in->readUint32LE();
+ decompressedEndOffset = in->readUint32LE();
+
+ // Read the compression type for NIS files
+ if (!isSequence) {
+ in->seek(basePos + 0x124);
+ } else {
+ hotspot.left = (int16)in->readUint16LE();
+ hotspot.right = (int16)in->readUint16LE();
+ hotspot.top = (int16)in->readUint16LE();
+ hotspot.bottom = (int16)in->readUint16LE();
+ }
+
+ compressionType = in->readByte();
+ subType = (FrameSubType)in->readByte();
+
+ // Sequence information
+ field_2E = in->readByte();
+ keepPreviousFrame = in->readByte();
+ field_30 = in->readByte();
+ field_31 = in->readByte();
+ soundAction = in->readByte();
+ field_33 = in->readByte();
+ position = in->readByte();
+ field_35 = in->readByte();
+ field_36 = in->readUint16LE();
+ field_38 = in->readUint32LE();
+ entityPosition = (EntityPosition)in->readUint16LE();
+ location = in->readUint16LE();
+ next = in->readUint32LE();
+}
+
+
+// AnimFrame
+
+AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) {
+ _palSize = 1;
+ // TODO: use just the needed rectangle
+ _image.create(640, 480, 1);
+
+ //debugC(6, kLastExpressDebugGraphics, " Offsets: data=%d, unknown=%d, palette=%d", f.dataOffset, f.unknown, f.paletteOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Position: (%d, %d) - (%d, %d)", f.xPos1, f.yPos1, f.xPos2, f.yPos2);
+ //debugC(6, kLastExpressDebugGraphics, " Initial Skip: %d", f.initialSkip);
+ //debugC(6, kLastExpressDebugGraphics, " Decompressed end offset: %d", f.decompressedEndOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Hotspot: (%d, %d) x (%d, %d)\n", f.hotspot.left, f.hotspot.top, f.hotspot.right, f.hotspot.bottom);
+ //debugC(6, kLastExpressDebugGraphics, " Compression type: %u / %u", f.compressionType, f.subType);
+ //debugC(6, kLastExpressDebugGraphics, " Unknown: %u - %u - %u - %u - %u - %u - %u - %d", f.field_2E, f.field_2F, f.field_30, f.field_31, f.field_33, f.field_35, f.field_36, f.field_38);
+ //debugC(6, kLastExpressDebugGraphics, " Sound action: %u", f.soundAction);
+ //debugC(6, kLastExpressDebugGraphics, " Position: %d", f.position);
+ //debugC(6, kLastExpressDebugGraphics, " Entity Position: %d", f.entityPosition);
+ //debugC(6, kLastExpressDebugGraphics, " Location: %d", f.location);
+ //debugC(6, kLastExpressDebugGraphics, " next: %d", f.next);
+
+ switch (f.compressionType) {
+ case 0:
+ // Empty frame
+ break;
+ case 3:
+ decomp3(in, f);
+ break;
+ case 4:
+ decomp4(in, f);
+ break;
+ case 5:
+ decomp5(in, f);
+ break;
+ case 7:
+ decomp7(in, f);
+ break;
+ case 255:
+ decompFF(in, f);
+ break;
+ default:
+ error("Unknown frame compression: %d", f.compressionType);
+ }
+
+ readPalette(in, f);
+ _rect = Common::Rect((int16)f.xPos1, (int16)f.yPos1, (int16)f.xPos2, (int16)f.yPos2);
+ //_rect.debugPrint(0, "Frame rect:");
+}
+
+AnimFrame::~AnimFrame() {
+ _image.free();
+ delete[] _palette;
+}
+
+Common::Rect AnimFrame::draw(Graphics::Surface *s) {
+ byte *inp = (byte *)_image.pixels;
+ uint16 *outp = (uint16 *)s->pixels;
+ for (int i = 0; i < 640 * 480; i++, inp++, outp++) {
+ if (*inp)
+ *outp = _palette[*inp];
+ }
+ return _rect;
+}
+
+void AnimFrame::readPalette(Common::SeekableReadStream *in, const FrameInfo &f) {
+ // Read the palette
+ in->seek((int)f.paletteOffset);
+ _palette = new uint16[_palSize];
+ for (uint32 i = 0; i < _palSize; i++) {
+ _palette[i] = in->readUint16LE();
+ }
+}
+
+void AnimFrame::decomp3(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0x7, 3);
+}
+
+void AnimFrame::decomp4(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0xf, 4);
+}
+
+void AnimFrame::decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ opcode &= 0x3f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x3f;
+ if (opcode & 0x20) {
+ opcode = ((opcode & 0x1f) << 8) + in->readByte();
+ if (opcode & 0x1000) {
+ out += opcode & 0xfff;
+ continue;
+ }
+ }
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & mask;
+ opcode >>= shift;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp5(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (!(opcode & 0x1f)) {
+ opcode = (uint16)((opcode << 3) + in->readByte());
+ if (opcode & 0x400) {
+ // skip these 10 bits
+ out += (opcode & 0x3ff);
+ } else {
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & 0x1f;
+ opcode >>= 5;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp7(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ if (opcode & 0x20) {
+ opcode &= 0x1f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x1f;
+ if (opcode & 0x10) {
+ opcode = ((opcode & 0xf) << 8) + in->readByte();
+ if (opcode & 0x800) {
+ // skip these 11 bits
+ out += (opcode & 0x7ff);
+ continue;
+ }
+ }
+
+ // skip these 4 bits
+ out += opcode + 2;
+ }
+ } else {
+ opcode &= 0x3f;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ }
+ }
+}
+
+void AnimFrame::decompFF(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode < 0x80) {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ } else {
+ if (opcode < 0xf0) {
+ if (opcode < 0xe0) {
+ // copy old part
+ uint32 old = out + ((opcode & 0x7) << 8) + in->readByte() - 2048;
+ opcode = ((opcode >> 3) & 0xf) + 3;
+ for (int i = 0; i < opcode; i++, out++, old++) {
+ p[out] = p[old];
+ }
+ } else {
+ opcode = (opcode & 0xf) + 1;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ out += ((opcode & 0xf) << 8) + in->readByte();
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SEQUENCE
+//////////////////////////////////////////////////////////////////////////
+
+Sequence::~Sequence() {
+ reset();
+}
+
+void Sequence::reset() {
+ _frames.clear();
+ delete _stream;
+ _stream = NULL;
+}
+
+Sequence *Sequence::load(Common::String name, Common::SeekableReadStream *stream, byte field30) {
+ Sequence *sequence = new Sequence(name);
+
+ if (!sequence->load(stream, field30)) {
+ delete sequence;
+ return NULL;
+ }
+
+ return sequence;
+}
+
+bool Sequence::load(Common::SeekableReadStream *stream, byte field30) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ _field30 = field30;
+
+ // Keep stream for later decoding of sequence
+ _stream = stream;
+
+ // Read header to get the number of frames
+ _stream->seek(0);
+ uint32 numframes = _stream->readUint32LE();
+ uint32 unknown = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of frames in sequence: %d / unknown=0x%x", numframes, unknown);
+
+ // Store frames information
+ for (uint i = 0; i < numframes; i++) {
+
+ // Move stream to start of frame
+ _stream->seek((int32)(_sequenceHeaderSize + i * _sequenceFrameSize), SEEK_SET);
+ if (_stream->eos())
+ error("Couldn't seek to the current frame data");
+
+ // Check if there is enough data
+ if ((unsigned)(_stream->size() - _stream->pos()) < _sequenceFrameSize)
+ error("The sequence frame does not have a valid header");
+
+ FrameInfo info;
+ info.read(_stream, true);
+ _frames.push_back(info);
+ }
+
+ _isLoaded = true;
+
+ return true;
+}
+
+FrameInfo *Sequence::getFrameInfo(uint16 index) {
+ if (_frames.size() == 0)
+ error("Trying to decode a sequence before loading its data");
+
+ if (index > _frames.size() - 1)
+ error("Invalid sequence frame requested: %d, max %d", index, _frames.size() - 1);
+
+ return &_frames[index];
+}
+
+AnimFrame *Sequence::getFrame(uint16 index) {
+
+ FrameInfo *frame = getFrameInfo(index);
+
+ if (!frame)
+ return NULL;
+
+ // Skip "invalid" frames
+ if (frame->compressionType == 0)
+ return NULL;
+
+ debugC(9, kLastExpressDebugGraphics, "Decoding sequence %s: frame %d / %d", _name.c_str(), index, _frames.size() - 1);
+
+ return new AnimFrame(_stream, *frame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SequenceFrame
+SequenceFrame::~SequenceFrame() {
+ if (_dispose && _sequence) {
+ delete _sequence;
+ }
+
+ _sequence = NULL;
+}
+
+Common::Rect SequenceFrame::draw(Graphics::Surface *surface) {
+ if (!_sequence || _frame >= _sequence->count())
+ return Common::Rect();
+
+ AnimFrame *f = _sequence->getFrame(_frame);
+ if (!f)
+ return Common::Rect();
+
+ Common::Rect rect = f->draw(surface);
+
+ delete f;
+
+ return rect;
+}
+
+bool SequenceFrame::setFrame(uint16 frame) {
+ if (!_sequence)
+ return false;
+
+ if (frame < _sequence->count()) {
+ _frame = frame;
+ return true;
+ } else
+ return false;
+}
+
+bool SequenceFrame::nextFrame() {
+ return setFrame(_frame + 1);
+}
+
+FrameInfo *SequenceFrame::getInfo() {
+ if (!_sequence)
+ error("SequenceFrame::getFrameInfo: Invalid sequence!");
+
+ return _sequence->getFrameInfo(_frame);
+}
+
+Common::String SequenceFrame::getName() {
+ if (!_sequence)
+ error("SequenceFrame::getName: Invalid sequence!");
+
+ return _sequence->getName();
+}
+
+bool SequenceFrame::equal(const SequenceFrame *other) const {
+ return _sequence->getName() == other->_sequence->getName() && _frame == other->_frame;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/sequence.h b/engines/lastexpress/data/sequence.h
new file mode 100644
index 0000000000..25170993df
--- /dev/null
+++ b/engines/lastexpress/data/sequence.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SEQUENCE_H
+#define LASTEXPRESS_SEQUENCE_H
+
+/*
+ Sequence format (.SEQ / .NIS (frame header & data only))
+
+ uint32 {4} - Number of frames in sequence
+ uint32 {4} - Unknown
+
+ frames headers (68 bytes):
+ // for each frame
+ uint32 {4} - Data offset (from beginning of file)
+ uint32 {4} - Unknown
+ uint32 {4} - Palette offset (from beginning of file)
+ uint32 {4} - Top-left X coordinate
+ uint32 {4} - Top-left Y coordinate
+ uint32 {4} - Bottom-right X coordinate
+ uint32 {4} - Bottom-right Y coordinate
+ uint32 {4} - Initial offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 {4} - End of data after decompression
+
+ (for SEQ files only)
+ uint16 {2} - Hotspot left
+ uint16 {2} - Hotspot right
+ uint16 {2} - Hotspot top
+ uint16 {2} - Hotspot bottom
+ byte {1} - Compression type
+ byte {1} - Subtype (determines which set of decompression functions will be called) => 0, 1, 2, 3
+ byte {1} - Unknown
+ byte {1} - Unknown
+ uint16 {2} - Unknown
+ byte {1} - Sound action
+ byte {1} - Unknown
+ uint32 {4} - positionId
+ uint32 {4} - Unknown
+ uint16 {2} - Entity Position
+ uint16 {2} - Location (~z-order)
+ uint32 {4} - Next sequence in the linked list
+
+ (for NIS files: found at 0x124)
+ byte {1} - Compression type
+
+ palette data:
+ uint16 {x} - palette data (max size: 256)
+
+ data
+ byte {x} - compressed image data
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+enum FrameSubType {
+ kFrameTypeNone = 0,
+ kFrameType1 = 1,
+ kFrameType2 = 2,
+ kFrameType3 = 3
+};
+
+struct FrameInfo {
+ void read(Common::SeekableReadStream *in, bool isSequence);
+
+ uint32 dataOffset; ///< Data offset (from beginning of file)
+ uint32 unknown; ///< FIXME: unknown data
+ uint32 paletteOffset; ///< Palette offset (from beginning of file)
+ uint32 xPos1; ///< Top-left X coordinate
+ uint32 yPos1; ///< Top-left Y coordinate
+ uint32 xPos2; ///< Bottom-right X coordinate
+ uint32 yPos2; ///< Bottom-right Y coordinate
+ uint32 initialSkip; ///< Initial on-screen offset of decompressed data (doubled, since each pixel occupies one color word)
+ uint32 decompressedEndOffset; ///< End of data after decompression
+
+ // NIS frame headers end here. SEQ frame headers have additional 32 bytes of
+ // data, notably the compression type at the position outlined above in
+ // CompPos_SEQ
+
+ Common::Rect hotspot;
+
+ byte compressionType; ///< Type of frame compression (0x03, 0x04, 0x05, 0x07, 0xFF)
+ FrameSubType subType; ///< Subtype (byte)
+
+ byte field_2E;
+ byte keepPreviousFrame;
+ byte field_30;
+ byte field_31;
+ byte soundAction;
+ byte field_33;
+ Position position;
+ byte field_35;
+ int16 field_36;
+ uint32 field_38;
+ EntityPosition entityPosition;
+ uint16 location;
+ uint32 next;
+};
+
+class AnimFrame : public Drawable {
+public:
+ AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f);
+ ~AnimFrame();
+ Common::Rect draw(Graphics::Surface *s);
+
+private:
+ void decomp3(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp4(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift);
+ void decomp5(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decomp7(Common::SeekableReadStream *in, const FrameInfo &f);
+ void decompFF(Common::SeekableReadStream *in, const FrameInfo &f);
+ void readPalette(Common::SeekableReadStream *in, const FrameInfo &f);
+
+ Graphics::Surface _image;
+ uint16 _palSize;
+ uint16 *_palette;
+ Common::Rect _rect;
+};
+
+class Sequence {
+public:
+ Sequence(Common::String name) : _stream(NULL), _isLoaded(false), _name(name), _field30(15) {}
+ ~Sequence();
+
+ static Sequence *load(Common::String name, Common::SeekableReadStream *stream = NULL, byte field30 = 15);
+
+ bool load(Common::SeekableReadStream *stream, byte field30 = 15);
+
+ uint16 count() const { return (uint16)_frames.size(); }
+ AnimFrame *getFrame(uint16 index = 0);
+ FrameInfo *getFrameInfo(uint16 index = 0);
+
+ Common::String getName() { return _name; }
+ byte getField30() { return _field30; }
+
+ bool isLoaded() { return _isLoaded; }
+
+private:
+ static const uint32 _sequenceHeaderSize = 8;
+ static const uint32 _sequenceFrameSize = 68;
+
+ void reset();
+
+ Common::Array<FrameInfo> _frames;
+ Common::SeekableReadStream *_stream;
+ bool _isLoaded;
+
+ Common::String _name;
+ byte _field30; // used when copying sequences
+};
+
+class SequenceFrame : public Drawable {
+public:
+ SequenceFrame(Sequence *sequence, uint16 frame = 0, bool dispose = false) : _sequence(sequence), _frame(frame), _dispose(dispose) {}
+ ~SequenceFrame();
+
+ Common::Rect draw(Graphics::Surface *surface);
+
+ bool setFrame(uint16 frame);
+ uint32 getFrame() { return _frame; }
+ bool nextFrame();
+
+ Common::String getName();
+ FrameInfo *getInfo();
+
+ bool equal(const SequenceFrame *other) const;
+
+private:
+ Sequence *_sequence;
+ uint16 _frame;
+ bool _dispose;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SEQUENCE_H
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
new file mode 100644
index 0000000000..496bd58772
--- /dev/null
+++ b/engines/lastexpress/data/snd.cpp
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SND
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/debug.h"
+
+#include "sound/decoders/adpcm.h"
+#include "sound/audiostream.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Sound
+//////////////////////////////////////////////////////////////////////////
+SimpleSound::SimpleSound() : _size(0), _blocks(0), _blockSize(0) {}
+
+SimpleSound::~SimpleSound() {
+ stop();
+}
+
+// Stop the sound
+void SimpleSound::stop() const {
+ g_system->getMixer()->stopHandle(_handle);
+}
+
+void SimpleSound::loadHeader(Common::SeekableReadStream *in) {
+ _size = in->readUint32LE();
+ _blocks = in->readUint16LE();
+ debugC(5, kLastExpressDebugSound, " sound header data: size=\"%d\", %d blocks", _size, _blocks);
+
+ assert (_size % _blocks == 0);
+ _blockSize = _size / _blocks;
+}
+
+Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size) const {
+ return Audio::makeADPCMStream(in, DisposeAfterUse::YES, size, Audio::kADPCMMSImaLastExpress, 44100, 1, _blockSize);
+}
+
+void SimpleSound::play(Audio::AudioStream *as) {
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, as);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+StreamedSound::StreamedSound() {}
+StreamedSound::~StreamedSound() {}
+
+bool StreamedSound::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ loadHeader(stream);
+
+ // Start decoding the input stream
+ Audio::AudioStream *as = makeDecoder(stream, _size);
+
+ // Start playing the decoded audio stream
+ play(as);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// StreamedSound
+//////////////////////////////////////////////////////////////////////////
+AppendableSound::AppendableSound() : SimpleSound() {
+ // Create an audio stream where the decoded chunks will be appended
+ _as = Audio::makeQueuingAudioStream(44100, false);
+ _finished = false;
+
+ // Start playing the decoded audio stream
+ play(_as);
+
+ // Initialize the block size
+ // TODO: get it as an argument?
+ _blockSize = 739;
+}
+
+AppendableSound::~AppendableSound() {
+ finish();
+
+ _as = NULL;
+}
+
+void AppendableSound::queueBuffer(const byte *data, uint32 size) {
+ Common::MemoryReadStream *buffer = new Common::MemoryReadStream(data, size);
+ queueBuffer(buffer);
+}
+
+void AppendableSound::queueBuffer(Common::SeekableReadStream *bufferIn) {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ // Setup the ADPCM decoder
+ uint32 sizeIn = (uint32)bufferIn->size();
+ Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn);
+
+ // Queue the stream
+ _as->queueAudioStream(adpcm);
+}
+
+void AppendableSound::finish() {
+ if (!_as)
+ error("AppendableSound::queueBuffer - internal error: the audio stream is invalid!");
+
+ if (!_finished)
+ _as->finish();
+
+ _finished = true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/snd.h b/engines/lastexpress/data/snd.h
new file mode 100644
index 0000000000..2e0bc8c1b0
--- /dev/null
+++ b/engines/lastexpress/data/snd.h
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SND_H
+#define LASTEXPRESS_SND_H
+
+/*
+ Sound format (.SND / .LNK)
+
+ uint32 {4} - data size
+ uint16 {2} - number of blocks
+
+ // for each block
+ int16 {2} - initial sample
+ byte {1} - initial index
+ byte {1} - unused (00)
+ byte {x} - IMA ADPCM sample codes
+*/
+
+#include "common/stream.h"
+#include "sound/mixer.h"
+
+namespace Audio {
+ class AudioStream;
+ class QueuingAudioStream;
+}
+
+namespace LastExpress {
+
+class SimpleSound {
+public:
+ SimpleSound();
+ virtual ~SimpleSound();
+
+ void stop() const;
+
+protected:
+ void loadHeader(Common::SeekableReadStream *in);
+ Audio::AudioStream *makeDecoder(Common::SeekableReadStream *in, uint32 size) const;
+ void play(Audio::AudioStream *as);
+
+ uint32 _size; ///< data size
+ ///< - NIS: size of all blocks, including those located in the matching LNK file
+ ///< - LNK: size of the LNK file itself, including the header
+ ///< - SND: size of all blocks
+ uint16 _blocks; ///< number of blocks
+ uint32 _blockSize;
+ Audio::SoundHandle _handle;
+};
+
+class StreamedSound : public SimpleSound {
+public:
+ StreamedSound();
+ ~StreamedSound();
+
+ bool load(Common::SeekableReadStream *stream);
+};
+
+class AppendableSound : public SimpleSound {
+public:
+ AppendableSound();
+ ~AppendableSound();
+
+ void queueBuffer(const byte *data, uint32 size);
+ void queueBuffer(Common::SeekableReadStream *bufferIn);
+ void finish();
+
+private:
+ Audio::QueuingAudioStream *_as;
+ bool _finished;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SND_H
diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp
new file mode 100644
index 0000000000..67d6445ab9
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.cpp
@@ -0,0 +1,244 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Based on the Xentax Wiki documentation:
+// http://wiki.xentax.com/index.php/The_Last_Express_SBE
+
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitle
+//////////////////////////////////////////////////////////////////////////
+class Subtitle {
+public:
+ Subtitle() : _timeStart(0), _timeStop(0), _topLength(0), _topText(NULL),
+ _bottomLength(0), _bottomText(NULL) {}
+ ~Subtitle() { reset(); }
+
+ bool load(Common::SeekableReadStream *in);
+ Common::Rect draw(Graphics::Surface *surface, Font *font);
+
+ uint16 getTimeStart() const { return _timeStart; }
+ uint16 getTimeStop() const { return _timeStop; }
+
+private:
+ uint16 _timeStart; ///< display start time
+ uint16 _timeStop; ///< display stop time
+
+ uint16 _topLength; ///< top line length
+ uint16 *_topText; ///< bottom line length
+
+ uint16 _bottomLength; ///< top line (UTF-16 string)
+ uint16 *_bottomText; ///< bottom line (UTF-16 string)
+
+ void reset();
+};
+
+void Subtitle::reset() {
+ delete[] _topText;
+ delete[] _bottomText;
+ _topText = NULL;
+ _bottomText = NULL;
+}
+
+template<typename T>
+T *newArray(size_t n) {
+ if (n <= (size_t)-1 / sizeof(T))
+ return new T[n];
+
+ // n is too large
+ return NULL;
+}
+
+bool Subtitle::load(Common::SeekableReadStream *in) {
+ reset();
+
+ if (!in)
+ return false;
+
+ // Read the display times
+ _timeStart = in->readUint16LE();
+ _timeStop = in->readUint16LE();
+
+ // Read the text lengths
+ _topLength = in->readUint16LE();
+ _bottomLength = in->readUint16LE();
+
+ // Create the buffers
+ if (_topLength) {
+ _topText = newArray<uint16>(_topLength + 1);
+ if (!_topText)
+ return false;
+
+ _topText[_topLength] = 0;
+ }
+ if (_bottomLength) {
+ _bottomText = newArray<uint16>(_bottomLength + 1);
+ if (!_bottomText)
+ return false;
+
+ _bottomText[_bottomLength] = 0;
+ }
+
+ // Read the texts
+ for (int i = 0; i < _topLength; i++)
+ _topText[i] = in->readUint16LE();
+ for (int i = 0; i < _bottomLength; i++)
+ _bottomText[i] = in->readUint16LE();
+
+ debugC(9, kLastExpressDebugSubtitle, " %d -> %d:", _timeStart, _timeStop);
+ if (_topLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_topText);
+ if (_bottomLength)
+ debugC(9, kLastExpressDebugSubtitle, "\t%ls", (wchar_t *)_bottomText);
+
+ return true;
+}
+
+Common::Rect Subtitle::draw(Graphics::Surface *surface, Font *font) {
+ Common::Rect rectTop, rectBottom;
+
+ //FIXME find out proper subtitles coordinates (and hope it's hardcoded and not stored in the sequence or animation)
+ rectTop = font->drawString(surface, 100, 100, _topText, _topLength);
+ rectBottom = font->drawString(surface, 100, 300, _bottomText, _bottomLength);
+
+ rectTop.extend(rectBottom);
+
+ return rectTop;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SubtitleManager
+//////////////////////////////////////////////////////////////////////////
+SubtitleManager::SubtitleManager(Font *font) : _font(font), _maxTime(0), _currentIndex(-1), _lastIndex(-1) {}
+
+SubtitleManager::~SubtitleManager() {
+ reset();
+
+ // Zero passed pointers
+ _font = NULL;
+}
+
+void SubtitleManager::reset() {
+ for (int i = 0; i < (int)_subtitles.size(); i++)
+ delete _subtitles[i];
+
+ _subtitles.clear();
+ _currentIndex = -1;
+ _lastIndex = -1;
+}
+
+bool SubtitleManager::load(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ reset();
+
+ // Read header to get the number of subtitles
+ uint32 numSubtitles = stream->readUint16LE();
+ if (stream->eos())
+ error("Cannot read from subtitle file");
+
+ debugC(3, kLastExpressDebugSubtitle, "Number of subtitles in file: %d", numSubtitles);
+
+ // TODO: Check that stream contain enough data
+ //if (stream->size() < (signed)(numSubtitles * sizeof(SubtitleData))) {
+ //debugC(2, kLastExpressDebugSubtitle, "Subtitle file does not contain valid data!");
+ //return false;
+ //}
+
+ // Read the list of subtitles
+ _maxTime = 0;
+ for (uint i = 0; i < numSubtitles; i++) {
+ Subtitle *subtitle = new Subtitle();
+ if (!subtitle->load(stream)) {
+ // Failed to read this line
+ reset();
+
+ delete subtitle;
+
+ return false;
+ }
+
+ // Update the max time
+ if (subtitle->getTimeStop() > _maxTime)
+ _maxTime = subtitle->getTimeStop();
+
+ _subtitles.push_back(subtitle);
+ }
+
+ delete stream;
+
+ return true;
+}
+
+uint16 SubtitleManager::getMaxTime() const {
+ return _maxTime;
+}
+
+void SubtitleManager::setTime(uint16 time) {
+ _currentIndex = -1;
+
+ // Find the appropriate line to show
+ for (int16 i = 0; i < (int16)_subtitles.size(); i++) {
+ if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) {
+ // Keep the index of the line to show
+ _currentIndex = i;
+ return;
+ }
+ }
+}
+
+bool SubtitleManager::hasChanged() const {
+ // TODO: mark the old line rect as dirty
+ if (_currentIndex != _lastIndex)
+ return true;
+ else
+ return false;
+}
+
+Common::Rect SubtitleManager::draw(Graphics::Surface *surface) {
+ // Update the last drawn index
+ _lastIndex = _currentIndex;
+
+ // Return if we don't have to draw any line
+ if (_currentIndex == -1)
+ return Common::Rect();
+
+ // Draw the current line
+ assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size());
+ return _subtitles[_currentIndex]->draw(surface, _font);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/data/subtitle.h b/engines/lastexpress/data/subtitle.h
new file mode 100644
index 0000000000..9acb7068f1
--- /dev/null
+++ b/engines/lastexpress/data/subtitle.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SUBTITLE_H
+#define LASTEXPRESS_SUBTITLE_H
+
+/*
+ Subtitle format (.SBE)
+
+ uint16 {2} - number of subtitles
+
+ // for each subtitle
+ uint16 {2} - display start time
+ uint16 {2} - display stop time
+ uint16 {2} - top line length
+ uint16 {2} - bottom line length
+ byte {x} - top line (UTF-16 string)
+ byte {x} - bottom line (UTF-16 string)
+
+ Subtitles seem to be drawn on screen at (80, 420) x (560, 458)
+*/
+
+#include "lastexpress/drawable.h"
+
+#include "common/array.h"
+#include "common/stream.h"
+
+namespace LastExpress {
+
+class Font;
+class Subtitle;
+
+class SubtitleManager : public Drawable {
+public:
+ SubtitleManager(Font *font);
+ ~SubtitleManager();
+
+ bool load(Common::SeekableReadStream *stream);
+ uint16 getMaxTime() const;
+ void setTime(uint16 time);
+ bool hasChanged() const;
+ Common::Rect draw(Graphics::Surface *surface);
+
+private:
+ Common::Array<Subtitle *> _subtitles;
+ Font *_font;
+ uint16 _maxTime;
+
+ int16 _currentIndex;
+ int16 _lastIndex;
+
+ void reset();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SUBTITLE_H
diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp
new file mode 100644
index 0000000000..35a87cf14b
--- /dev/null
+++ b/engines/lastexpress/debug.cpp
@@ -0,0 +1,1178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/debug.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/md5.h"
+
+namespace LastExpress {
+
+Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL), _numParams(0), _commandParams(NULL) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Register the debugger commands
+
+ // General
+ DCmd_Register("help", WRAP_METHOD(Debugger, cmdHelp));
+
+ // Data
+ DCmd_Register("ls", WRAP_METHOD(Debugger, cmdListFiles));
+ DCmd_Register("dump", WRAP_METHOD(Debugger, cmdDumpFiles));
+
+ DCmd_Register("showframe", WRAP_METHOD(Debugger, cmdShowFrame));
+ DCmd_Register("showbg", WRAP_METHOD(Debugger, cmdShowBg));
+ DCmd_Register("playseq", WRAP_METHOD(Debugger, cmdPlaySeq));
+ DCmd_Register("playsnd", WRAP_METHOD(Debugger, cmdPlaySnd));
+ DCmd_Register("playsbe", WRAP_METHOD(Debugger, cmdPlaySbe));
+ DCmd_Register("playnis", WRAP_METHOD(Debugger, cmdPlayNis));
+
+ // Scene & interaction
+ DCmd_Register("loadscene", WRAP_METHOD(Debugger, cmdLoadScene));
+ DCmd_Register("fight", WRAP_METHOD(Debugger, cmdFight));
+ DCmd_Register("beetle", WRAP_METHOD(Debugger, cmdBeetle));
+
+ // Game
+ DCmd_Register("delta", WRAP_METHOD(Debugger, cmdTimeDelta));
+ DCmd_Register("time", WRAP_METHOD(Debugger, cmdTime));
+ DCmd_Register("show", WRAP_METHOD(Debugger, cmdShow));
+ DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity));
+
+ // Misc
+ DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame));
+ DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter));
+ DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear));
+
+ resetCommand();
+
+ _soundStream = new StreamedSound();
+}
+
+Debugger::~Debugger() {
+ DebugMan.clearAllDebugChannels();
+
+ delete _soundStream;
+ resetCommand();
+
+ _command = NULL;
+ _commandParams = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::hasCommand() const {
+ return (_numParams != 0);
+}
+
+void Debugger::resetCommand() {
+ SAFE_DELETE(_command);
+
+ if (_commandParams)
+ for (int i = 0; i < _numParams; i++)
+ free(_commandParams[i]);
+
+ free(_commandParams);
+ _commandParams = NULL;
+ _numParams = 0;
+}
+
+int Debugger::getNumber(const char *arg) const {
+ return strtol(arg, (char **)NULL, 0);
+}
+
+void Debugger::copyCommand(int argc, const char **argv) {
+ _commandParams = (char **)malloc(sizeof(char *) * (uint)argc);
+ if (!_commandParams)
+ return;
+
+ _numParams = argc;
+
+ for (int i = 0; i < _numParams; i++) {
+ _commandParams[i] = (char *)malloc(strlen(argv[i]) + 1);
+ memset(_commandParams[i], 0, strlen(argv[i]) + 1);
+ strcpy(_commandParams[i], argv[i]);
+ }
+
+ // Exit the debugger!
+ Cmd_Exit(0, 0);
+}
+
+void Debugger::callCommand() {
+ if (_command)
+ (*_command)(_numParams, const_cast<const char **>(_commandParams));
+}
+
+void Debugger::loadArchive(ArchiveIndex index) const {
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+// Restore loaded archive
+void Debugger::restoreArchive() const {
+
+ ArchiveIndex index = kArchiveCd1;
+
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ index = kArchiveCd1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ _engine->getResourceManager()->loadArchive(index);
+ getScenes()->loadSceneDataFile(index);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Debugger commands
+//////////////////////////////////////////////////////////////////////////
+bool Debugger::cmdHelp(int, const char **) {
+ DebugPrintf("Debug flags\n");
+ DebugPrintf("-----------\n");
+ DebugPrintf(" debugflag_list - Lists the available debug flags and their status\n");
+ DebugPrintf(" debugflag_enable - Enables a debug flag\n");
+ DebugPrintf(" debugflag_disable - Disables a debug flag\n");
+ DebugPrintf("\n");
+ DebugPrintf("Commands\n");
+ DebugPrintf("--------\n");
+ DebugPrintf(" ls - list files in the archive\n");
+ DebugPrintf(" dump - dump a list of files in all archives\n");
+ DebugPrintf("\n");
+ DebugPrintf(" showframe - show a frame from a sequence\n");
+ DebugPrintf(" showbg - show a background\n");
+ DebugPrintf(" playseq - play a sequence\n");
+ DebugPrintf(" playsnd - play a sound\n");
+ DebugPrintf(" playsbe - play a subtitle\n");
+ DebugPrintf(" playnis - play an animation\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadscene - load a scene\n");
+ DebugPrintf(" fight - start a fight\n");
+ DebugPrintf(" beetle - start the beetle game\n");
+ DebugPrintf("\n");
+ DebugPrintf(" delta - Adjust the time delta\n");
+ DebugPrintf(" show - show game data\n");
+ DebugPrintf(" entity - show entity data\n");
+ DebugPrintf("\n");
+ DebugPrintf(" loadgame - load a saved game\n");
+ DebugPrintf(" chapter - switch to a specific chapter\n");
+ DebugPrintf(" clear - clear the screen\n");
+ DebugPrintf("\n");
+ return true;
+}
+
+/**
+ * Command: list files in archive
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdListFiles(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filter(const_cast<char *>(argv[1]));
+
+ // Load the proper archive
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ Common::ArchiveMemberList list;
+ int count = _engine->getResourceManager()->listMatchingMembers(list, filter);
+
+ DebugPrintf("Number of matches: %d\n", count);
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it)
+ DebugPrintf(" %s\n", (*it)->getName().c_str());
+
+ // Restore archive
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: ls <filter> (use * for all) (<cd number>)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Dump the list of files in the archive
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdDumpFiles(int argc, const char **) {
+#define OUTPUT_ARCHIVE_FILES(name, filename) { \
+ _engine->getResourceManager()->reset(); \
+ _engine->getResourceManager()->loadArchive(filename); \
+ Common::ArchiveMemberList list; \
+ int count = _engine->getResourceManager()->listMatchingMembers(list, "*"); \
+ debugC(1, kLastExpressDebugResource, "\n\n--------------------------------------------------------------------\n"); \
+ debugC(1, kLastExpressDebugResource, "-- " #name " (%d files)\n", count); \
+ debugC(1, kLastExpressDebugResource, "--------------------------------------------------------------------\n\n"); \
+ debugC(1, kLastExpressDebugResource, "Filename,Size,MD5\n"); \
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) { \
+ Common::SeekableReadStream *stream = getArchive((*it)->getName()); \
+ if (!stream) { \
+ DebugPrintf("ERROR: Cannot create stream for file: %s\n", (*it)->getName().c_str()); \
+ restoreArchive(); \
+ return true; \
+ } \
+ char md5str[32+1]; \
+ Common::md5_file_string(*stream, md5str, (uint32)stream->size()); \
+ debugC(1, kLastExpressDebugResource, "%s, %d, %s", (*it)->getName().c_str(), stream->size(), (char *)&md5str); \
+ delete stream; \
+ } \
+}
+
+ if (argc == 1) {
+ // For each archive file, dump the list of files
+ if (_engine->isDemo()) {
+ OUTPUT_ARCHIVE_FILES("DEMO", "DEMO.HPF");
+ } else {
+ OUTPUT_ARCHIVE_FILES("HD", "HD.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 1", "CD1.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 2", "CD2.HPF");
+ OUTPUT_ARCHIVE_FILES("CD 3", "CD3.HPF");
+ }
+
+ // Restore current loaded archive
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: dump");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Shows a frame
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowFrame(int argc, const char **argv) {
+ if (argc == 3 || argc == 4) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 4)
+ loadArchive((ArchiveIndex)getNumber(argv[3]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowFrame);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence sequence(filename);
+ if (sequence.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ AnimFrame *frame = sequence.getFrame((uint16)getNumber(argv[2]));
+ if (!frame) {
+ DebugPrintf("Invalid frame index: %i\n", filename.c_str());
+ resetCommand();
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(frame, GraphicsManager::kBackgroundOverlay);
+ delete frame;
+
+ askForRedraw();
+ redrawScreen();
+
+ _engine->_system->delayMillis(1000);
+ _engine->getCursor()->show(true);
+ }
+
+ resetCommand();
+
+ if (argc == 4)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: cmd_showframe <seqname> <index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: shows a background
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShowBg(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename + ".BG")) {
+ DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdShowBg);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ clearBg(GraphicsManager::kBackgroundC);
+
+ Background *background = _engine->getResourceManager()->loadBackground(filename);
+ if (background) {
+ _engine->getGraphicsManager()->draw(background, GraphicsManager::kBackgroundC);
+ delete background;
+ askForRedraw();
+ }
+
+ redrawScreen();
+
+ if (argc == 3)
+ restoreArchive();
+
+ // Pause for a second to be able to see the background
+ _engine->_system->delayMillis(1000);
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: showbg <bgname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySeq(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+ filename += ".seq";
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySeq);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ Sequence *sequence = new Sequence(filename);
+ if (sequence->load(getArchive(filename))) {
+
+ // Check that we have at least a frame to show
+ if (sequence->count() == 0) {
+ delete sequence;
+ return false;
+ }
+
+ _engine->getCursor()->show(false);
+
+ SequenceFrame player(sequence, 0, true);
+ do {
+ // Clear screen
+ clearBg(GraphicsManager::kBackgroundA);
+
+ _engine->getGraphicsManager()->draw(&player, GraphicsManager::kBackgroundA);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(175);
+
+ // go to the next frame
+ } while (player.nextFrame());
+ _engine->getCursor()->show(true);
+ } else {
+ // Sequence player is deleting his reference to the sequence, but we need to take care of it if the
+ // sequence could not be loaded
+ delete sequence;
+ }
+
+ resetCommand();
+
+ if (argc == 3)
+ restoreArchive();
+ }
+ } else {
+ DebugPrintf("Syntax: playseq <seqname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a sound
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySnd(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // Add .SND at the end of the filename if needed
+ Common::String name(const_cast<char *>(argv[1]));
+ if (!name.contains('.'))
+ name += ".SND";
+
+ if (!_engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ _engine->_system->getMixer()->stopAll();
+
+ _soundStream->load(getArchive(name));
+
+ if (argc == 3)
+ restoreArchive();
+ } else {
+ DebugPrintf("Syntax: playsnd <sndname> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays subtitles
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlaySbe(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String filename(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ filename += ".sbe";
+
+ if (!_engine->getResourceManager()->hasFile(filename)) {
+ DebugPrintf("Cannot find file: %s\n", filename.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlaySbe);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ SubtitleManager subtitle(_engine->getFont());
+ if (subtitle.load(getArchive(filename))) {
+ _engine->getCursor()->show(false);
+ for (uint16 i = 0; i < subtitle.getMaxTime(); i += 25) {
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ subtitle.setTime(i);
+ _engine->getGraphicsManager()->draw(&subtitle, GraphicsManager::kBackgroundOverlay);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Handle right-click to interrupt sequence
+ Common::Event ev;
+ _engine->getEventManager()->pollEvent(ev);
+ if (ev.type == Common::EVENT_RBUTTONUP)
+ break;
+
+ _engine->_system->delayMillis(500);
+ }
+ _engine->getCursor()->show(true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playsbe <sbename> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: plays a NIS animation sequence.
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdPlayNis(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ Common::String name(const_cast<char *>(argv[1]));
+
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ // If we got a nis filename, check that the file exists
+ if (name.contains('.') && _engine->getResourceManager()->hasFile(name)) {
+ DebugPrintf("Cannot find file: %s\n", name.c_str());
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdPlayNis);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+
+ // Check if we got a nis filename or an animation index
+ if (name.contains('.')) {
+ Animation animation;
+ if (animation.load(getArchive(name))) {
+ _engine->getCursor()->show(false);
+ animation.play();
+ _engine->getCursor()->show(true);
+ }
+ } else {
+ getAction()->playAnimation((EventIndex)atoi(name.c_str()), true);
+ }
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: playnis <nisname.nis or animation index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: loads a scene
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadScene(int argc, const char **argv) {
+ if (argc == 2 || argc == 3) {
+ int cd = 1;
+ SceneIndex index = (SceneIndex)getNumber(argv[1]);
+
+ // Check args
+ if (argc == 3)
+ loadArchive((ArchiveIndex)getNumber(argv[2]));
+
+ if (index > 2500) {
+ DebugPrintf("Error: invalid index value (0-2500)");
+ return true;
+ }
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdLoadScene);
+ copyCommand(argc, argv);
+
+ return Cmd_Exit(0, 0);
+ } else {
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ /************ DEBUG *************************/
+ // Use to find scenes with certain values
+
+ //for (int i = index; i < 2500; i++) {
+ // loadSceneObject(scene, i);
+
+ // if (scene.getHeader() && scene.getHeader()->car == 5 && scene.getHeader()->position == 81) {
+ // DebugPrintf("Found scene: %d", i);
+
+ // // Draw scene found
+ // _engine->getGraphicsManager()->draw(&scene, GraphicsManager::kBackgroundC);
+
+ // askForRedraw();
+ // redrawScreen();
+ // _engine->_system->delayMillis(500);
+
+ // break;
+ // }
+ //}
+
+ //delete _sceneLoader;
+ //resetCommand();
+ //return true;
+
+ /*********************************************/
+ Scene *scene = getScenes()->get(index);
+ if (!scene) {
+ DebugPrintf("Cannot load scene %i from CD %i", index, cd);
+ resetCommand();
+
+ return true;
+ }
+
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ // Pause for a second to be able to see the scene
+ _engine->_system->delayMillis(500);
+
+ if (argc == 3)
+ restoreArchive();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: loadscene <scene index> (<cd number>)\n");
+ }
+ return true;
+}
+
+/**
+ * Command: starts a fight sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdFight(int argc, const char **argv) {
+ if (argc == 2) {
+ FightType type = (FightType)getNumber(argv[1]);
+
+ // Load proper data file
+ ArchiveIndex index = kArchiveCd1;
+ switch (type) {
+ default:
+ goto error;
+
+ case kFightMilos:
+ index = kArchiveCd1;
+ break;
+
+ case kFightAnna:
+ index = kArchiveCd2;
+ break;
+
+ case kFightIvo:
+ case kFightSalko:
+ case kFightVesna:
+ index = kArchiveCd3;
+ break;
+ }
+
+ loadArchive(index);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdFight);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Make sure we are not called in a loop
+ _numParams = 0;
+
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ SceneIndex lastScene = getState()->scene;
+
+ getFight()->setup(type) ? DebugPrintf("Lost fight!\n") : DebugPrintf("Won fight!\n");
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *scene = getScenes()->get(lastScene);
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: fight <id> (id=2001-2005)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: starts the beetle sequence
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdBeetle(int argc, const char **argv) {
+ if (argc == 1) {
+ // Load proper data file (beetle game in in Cd2)
+ loadArchive(kArchiveCd2);
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdBeetle);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+
+ // Save current state
+ SceneIndex previousScene = getState()->scene;
+ ObjectLocation previousLocation = getInventory()->get(kItemBeetle)->location;
+ ChapterIndex previousChapter = (ChapterIndex)getProgress().chapter;
+
+ // Setup scene & inventory
+ getProgress().chapter = kChapter2;
+ Scene *scene = getScenes()->get(kSceneBeetle);
+ getInventory()->get(kItemBeetle)->location = kObjectLocation3;
+
+ askForRedraw();
+ redrawScreen();
+
+ // Load the beetle game
+ Action *action = NULL;
+ Beetle *beetle = new Beetle(_engine);
+ if (!beetle->isLoaded())
+ beetle->load();
+
+ // Play the game
+ Common::Event ev;
+ bool playgame = true;
+ while (playgame) {
+ // Update beetle
+ beetle->update();
+
+ askForRedraw();
+ redrawScreen();
+
+ while (g_engine->getEventManager()->pollEvent(ev)) {
+
+ switch (ev.type) {
+ default:
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ // Exit beetle game on escape
+ if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ playgame = false;
+
+ break;
+
+ case Common::EVENT_MOUSEMOVE: {
+ // Update cursor
+ CursorStyle style = kCursorNormal;
+ SceneHotspot *hotspot = NULL;
+ if (scene->checkHotSpot(ev.mouse, &hotspot)) {
+ if (!action)
+ action = new Action(_engine);
+
+ style = action->getCursor(*hotspot);
+ }
+
+ _engine->getCursor()->setStyle(style);
+ break;
+ }
+
+
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ // Update coordinates
+ getLogic()->getGameState()->setCoordinates(ev.mouse);
+
+ if (beetle->catchBeetle())
+ playgame = false;
+ break;
+ }
+
+ _engine->_system->delayMillis(10);
+ }
+ }
+
+ // Cleanup
+ beetle->unload();
+ delete beetle;
+ delete action;
+
+ // Pause for a second to be able to see the final scene
+ _engine->_system->delayMillis(1000);
+
+ // Restore state
+ getProgress().chapter = previousChapter;
+ getInventory()->get(kItemBeetle)->location = previousLocation;
+
+ // Restore loaded archive
+ restoreArchive();
+
+ // Stop audio and restore scene
+ getSound()->stopAllSound();
+
+ clearBg(GraphicsManager::kBackgroundAll);
+
+ Scene *oldScene = getScenes()->get(previousScene);
+ _engine->getGraphicsManager()->draw(oldScene, GraphicsManager::kBackgroundC);
+
+ askForRedraw();
+ redrawScreen();
+
+ resetCommand();
+ }
+ } else {
+ DebugPrintf("Syntax: beetle\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: adjusts the time delta
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdTimeDelta(int argc, const char **argv) {
+ if (argc == 2) {
+ int delta = getNumber(argv[1]);
+
+ if (delta <= 0 || delta > 500)
+ goto label_error;
+
+ getState()->timeDelta = (uint)delta;
+ } else {
+label_error:
+ DebugPrintf("Syntax: delta <time delta> (delta=1-500)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: Convert between in-game time and human readable time
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdTime(int argc, const char **argv) {
+ if (argc == 2) {
+ int32 time = getNumber(argv[1]);
+
+ if (time < 0)
+ goto label_error;
+
+ // Convert to human-readable form
+ uint8 hours = 0;
+ uint8 minutes = 0;
+ State::getHourMinutes((uint32)time, &hours, &minutes);
+
+ DebugPrintf("%02d:%02d\n", hours, minutes);
+ } else {
+label_error:
+ DebugPrintf("Syntax: time <time to convert> (time=0-INT_MAX)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: show game logic data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdShow(int argc, const char **argv) {
+#define OUTPUT_DUMP(name, text) \
+ DebugPrintf(#name "\n"); \
+ DebugPrintf("--------------------------------------------------------------------\n\n"); \
+ DebugPrintf(text); \
+ DebugPrintf("\n");
+
+ if (argc == 2) {
+
+ Common::String name(const_cast<char *>(argv[1]));
+
+ if (name == "state" || name == "st") {
+ OUTPUT_DUMP("Game state", getState()->toString().c_str());
+ } else if (name == "progress" || name == "pr") {
+ OUTPUT_DUMP("Progress", getProgress().toString().c_str());
+ } else if (name == "flags" || name == "fl") {
+ OUTPUT_DUMP("Flags", getFlags()->toString().c_str());
+ } else if (name == "inventory" || name == "inv") {
+ OUTPUT_DUMP("Inventory", getInventory()->toString().c_str());
+ } else if (name == "objects" || name == "obj") {
+ OUTPUT_DUMP("Objects", getObjects()->toString().c_str());
+ } else if (name == "savepoints" || name == "pt") {
+ OUTPUT_DUMP("SavePoints", getSavePoints()->toString().c_str());
+ } else if (name == "scene" || name == "sc") {
+ OUTPUT_DUMP("Current scene", getScenes()->get(getState()->scene)->toString().c_str());
+ } else {
+ goto label_error;
+ }
+
+ } else {
+label_error:
+ DebugPrintf("Syntax: state <option>\n");
+ DebugPrintf(" state / st\n");
+ DebugPrintf(" progress / pr\n");
+ DebugPrintf(" flags / fl\n");
+ DebugPrintf(" inventory / inv\n");
+ DebugPrintf(" objects / obj\n");
+ DebugPrintf(" savepoints / pt\n");
+ DebugPrintf(" scene / sc\n");
+ }
+
+ return true;
+
+#undef OUTPUT_DUMP
+}
+
+/**
+ * Command: shows entity data
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdEntity(int argc, const char **argv) {
+ if (argc == 2) {
+ EntityIndex index = (EntityIndex)getNumber(argv[1]);
+
+ if (index > 39)
+ goto label_error;
+
+ DebugPrintf("Entity %s\n", ENTITY_NAME(index));
+ DebugPrintf("--------------------------------------------------------------------\n\n");
+ DebugPrintf(getEntities()->getData(index)->toString().c_str());
+
+ // The Player entity does not have any callback data
+ if (index != kEntityPlayer) {
+ EntityData *data = getEntities()->get(index)->getParamData();
+ for (uint callback = 0; callback < 9; callback++) {
+ DebugPrintf("Call parameters %d:\n", callback);
+ for (byte ix = 0; ix < 4; ix++)
+ DebugPrintf(" %s", data->getParameters(callback, ix)->toString().c_str());
+ }
+ }
+
+ DebugPrintf("\n");
+ } else {
+label_error:
+ DebugPrintf("Syntax: entity <index>\n");
+ for (int i = 0; i < 40; i += 4)
+ DebugPrintf(" %s - %d %s - %d %s - %d %s - %d\n", ENTITY_NAME(i), i, ENTITY_NAME(i+1), i+1, ENTITY_NAME(i+2), i+2, ENTITY_NAME(i+3), i+3);
+ }
+
+ return true;
+}
+
+/**
+ * Command: loads a game
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdLoadGame(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id == 0 || id > 6)
+ goto error;
+
+ getSaveLoad()->loadGame((GameId)(id - 1));
+ } else {
+error:
+ DebugPrintf("Syntax: loadgame <id> (id=1-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: switch to a specific chapter
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdSwitchChapter(int argc, const char **argv) {
+ if (argc == 2) {
+ int id = getNumber(argv[1]);
+
+ if (id <= 1 || id > 6)
+ goto error;
+
+ // Store command
+ if (!hasCommand()) {
+ _command = WRAP_METHOD(Debugger, cmdSwitchChapter);
+ copyCommand(argc, argv);
+
+ return false;
+ } else {
+ // Sets the current chapter and then call Logic::switchChapter to proceed to the next chapter
+ getState()->progress.chapter = (ChapterIndex)(id - 1);
+
+ getLogic()->switchChapter();
+
+ resetCommand();
+ }
+ } else {
+error:
+ DebugPrintf("Syntax: chapter <id> (id=2-6)\n");
+ }
+
+ return true;
+}
+
+/**
+ * Command: clears the screen
+ *
+ * @param argc The argument count.
+ * @param argv The values.
+ *
+ * @return true if it was handled, false otherwise
+ */
+bool Debugger::cmdClear(int argc, const char **) {
+ if (argc == 1) {
+ clearBg(GraphicsManager::kBackgroundAll);
+ askForRedraw();
+ redrawScreen();
+ } else {
+ DebugPrintf("Syntax: clear - clear the screen\n");
+ }
+
+ return true;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h
new file mode 100644
index 0000000000..e935b35fc0
--- /dev/null
+++ b/engines/lastexpress/debug.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_DEBUG_H
+#define LASTEXPRESS_DEBUG_H
+
+#include "gui/debugger.h"
+
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+enum {
+ kLastExpressDebugAll = 1 << 0,
+ kLastExpressDebugGraphics = 1 << 1,
+ kLastExpressDebugResource = 1 << 2,
+ kLastExpressDebugCursor = 1 << 3,
+ kLastExpressDebugSound = 1 << 4,
+ kLastExpressDebugSubtitle = 1 << 5,
+ kLastExpressDebugSavegame = 1 << 6,
+ kLastExpressDebugLogic = 1 << 7,
+ kLastExpressDebugScenes = 1 << 8,
+ kLastExpressDebugUnknown = 1 << 9
+ // the current limitation is 32 debug levels (1 << 31 is the last one)
+};
+
+class LastExpressEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(LastExpressEngine *engine);
+ ~Debugger();
+
+ bool hasCommand() const;
+ void callCommand();
+
+private:
+ LastExpressEngine *_engine;
+
+ bool cmdHelp(int argc, const char **argv);
+
+ bool cmdListFiles(int argc, const char **argv);
+ bool cmdDumpFiles(int argc, const char **argv);
+
+ bool cmdShowFrame(int argc, const char **argv);
+ bool cmdShowBg(int argc, const char **argv);
+ bool cmdPlaySeq(int argc, const char **argv);
+ bool cmdPlaySnd(int argc, const char **argv);
+ bool cmdPlaySbe(int argc, const char **argv);
+ bool cmdPlayNis(int argc, const char **argv);
+
+ bool cmdLoadScene(int argc, const char **argv);
+ bool cmdFight(int argc, const char **argv);
+ bool cmdBeetle(int argc, const char **argv);
+
+ bool cmdTimeDelta(int argc, const char **argv);
+ bool cmdTime(int argc, const char **argv);
+ bool cmdShow(int argc, const char **argv);
+ bool cmdEntity(int argc, const char **argv);
+
+ bool cmdLoadGame(int argc, const char **argv);
+ bool cmdSwitchChapter(int argc, const char **argv);
+ bool cmdClear(int argc, const char **argv);
+
+ void resetCommand();
+ void copyCommand(int argc, const char **argv);
+ int getNumber(const char *arg) const;
+
+ void loadArchive(ArchiveIndex index) const;
+ void restoreArchive() const;
+
+ Debuglet *_command;
+ int _numParams;
+ char **_commandParams;
+
+ // Special sound stream for playing sounds
+ StreamedSound *_soundStream;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DEBUG_H
diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp
new file mode 100644
index 0000000000..d72a5eab86
--- /dev/null
+++ b/engines/lastexpress/detection.cpp
@@ -0,0 +1,211 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+static const PlainGameDescriptor lastExpressGames[] = {
+ // Games
+ {"lastexpress", "The Last Express"},
+ {0, 0}
+};
+
+static const ADGameDescription gameDescriptions[] = {
+
+ // The Last Express (English) - US Broderbund Release
+ // expressw.exe 1997-02-12 17:24:44
+ // express.exe 1997-02-12 17:29:08
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "2d331459e0e68cf277ef4e4043750413", 29865984}, // 1997-02-10 19:38:19
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944}, // 1997-02-10 17:04:40
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312}, // 1997-02-10 16:19:30
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448}, // 1997-02-10 15:44:09
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (English) - UK Broderbund Release
+ // expressw.exe 1997-04-02 14:30:32
+ // express.exe 1997-04-02 15:00:50
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "2d331459e0e68cf277ef4e4043750413", 29865984}, // 1997-04-10 11:03:41
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944}, // 1997-04-10 11:03:36
+ {"CD2.HPF", 0, "2672348691e1ae22d37d9f46f3683a07", 669509632}, // 1997-04-11 09:48:33
+ {"CD3.HPF", 0, "33f5e35f51063cb90f6bed9974475aa6", 641056768}, // 1997-04-11 09:48:55
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (English) - Interplay Release
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "bcc32d977f92bb52c060a0b4e8589cac", 30715904},
+ {"CD1.HPF", 0, "8c86db47304033fcff32c69fddd5a920", 525522944},
+ {"CD2.HPF", 0, "58aa26e782d10ec5d2231e539d2fe6a2", 669581312},
+ {"CD3.HPF", 0, "00554fbf78a2ad391d98578fbbbe1c48", 641128448},
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (French) - Broderbund Release
+ // expressw.exe 1997-04-02 09:31:24
+ // express.exe 1997-04-02 10:01:12
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "c14c6d685d9bf8705d9f659062e6c5c2", 29505536}, // 1997-04-03 07:53:20
+ {"CD1.HPF", 0, "b4277b22bc5cd6ad3b00c2ec04d4645d", 522924032}, // 1997-04-03 07:53:14
+ {"CD2.HPF", 0, "8c9610aa4cb707ab51f61c30feb22c1a", 665710592}, // 1997-04-09 12:04:30
+ {"CD3.HPF", 0, "411c1bab57b3e8da4fb359c5b40ef5d7", 640884736}, // 1997-04-03 08:21:47
+ },
+ Common::FR_FRA,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (German)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "7cdd70fc0b1555785f1e9e8d371ea85c", 31301632},
+ {"CD1.HPF", 0, "6d74cc861d172466bc745ff8bf0e59c5", 522971136},
+ {"CD2.HPF", 0, "b71ac9391de415807c74ff078f4fab22", 655702016},
+ {"CD3.HPF", 0, "ee55d4310546dd2a38560b096d1c2771", 641144832},
+ },
+ Common::DE_DEU,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Spanish)
+ {
+ "lastexpress",
+ "",
+ {
+ {"HD.HPF", 0, "46bed8832f06cf7160883a2aae2d667f", 29657088},
+ {"CD1.HPF", 0, "367a3a8581f6f88ddc51af7cde105ba9", 519927808},
+ {"CD2.HPF", 0, "af5566df3000472852ec182c9ec57797", 662210560},
+ {"CD3.HPF", 0, "0d1901662f4d063a5c250c9fbf64b771", 639504384},
+ },
+ Common::ES_ESP,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+
+ // The Last Express (Demo) - Broderbund
+ // expressw.exe 1997-08-14 14:09:42
+ // express.exe 1997-08-14 14:19:34
+ {
+ "lastexpress",
+ "Demo",
+ {
+ {"Demo.HPF", 0, "baf3b1f64155d34872896e61c3d3cb78", 58191872}, // 1997-08-14 14:44:26
+ },
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)gameDescriptions,
+ // Size of that superset structure
+ sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ lastExpressGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ "lastexpress",
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
+};
+
+
+class LastExpressMetaEngine : public AdvancedMetaEngine {
+public:
+ LastExpressMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ const char *getName() const {
+ return "LastExpress Engine";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "LastExpress Engine (C) 1997 Smoking Car Productions";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+};
+
+bool LastExpressMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (gd) {
+ *engine = new LastExpressEngine(syst, (const ADGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+} // End of namespace LastExpress
+
+#if PLUGIN_ENABLED_DYNAMIC(LASTEXPRESS)
+ REGISTER_PLUGIN_DYNAMIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(LASTEXPRESS, PLUGIN_TYPE_ENGINE, LastExpress::LastExpressMetaEngine);
+#endif
diff --git a/tools/sci/scriptdump.cpp b/engines/lastexpress/drawable.h
index 9a5ef00035..e273876362 100644
--- a/tools/sci/scriptdump.cpp
+++ b/engines/lastexpress/drawable.h
@@ -23,23 +23,20 @@
*
*/
-#include <console.h>
-#include <script.h>
-#include <vocabulary.h>
-#include <old_objects.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <engine.h>
-#include "sciunpack.h"
-
-int script_dump() {
- con_passthrough = 1;
-
- if (loadObjects(resmgr)) {
- fprintf(stderr, "Unable to load object hierarchy\n");
- return 1;
- }
-
- printObject(object_root, SCRIPT_PRINT_METHODS | SCRIPT_PRINT_CHILDREN);
- return 0;
-}
+#ifndef LASTEXPRESS_DRAWABLE_H
+#define LASTEXPRESS_DRAWABLE_H
+
+#include "graphics/surface.h"
+
+namespace LastExpress {
+
+class Drawable {
+public:
+ virtual ~Drawable() {}
+
+ virtual Common::Rect draw(Graphics::Surface *surface) = 0;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_DRAWABLE_H
diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp
new file mode 100644
index 0000000000..777767600f
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.cpp
@@ -0,0 +1,1910 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/abbot.h"
+
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) {
+ ADD_CALLBACK_FUNCTION(Abbot, reset);
+ ADD_CALLBACK_FUNCTION(Abbot, draw);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Abbot, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Abbot, draw2);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Abbot, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Abbot, playSound);
+ ADD_CALLBACK_FUNCTION(Abbot, savegame);
+ ADD_CALLBACK_FUNCTION(Abbot, updateEntity);
+ ADD_CALLBACK_FUNCTION(Abbot, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Abbot, updatePosition);
+ ADD_CALLBACK_FUNCTION(Abbot, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter1);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter2);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function19);
+ ADD_CALLBACK_FUNCTION(Abbot, function20);
+ ADD_CALLBACK_FUNCTION(Abbot, function21);
+ ADD_CALLBACK_FUNCTION(Abbot, function22);
+ ADD_CALLBACK_FUNCTION(Abbot, function23);
+ ADD_CALLBACK_FUNCTION(Abbot, function24);
+ ADD_CALLBACK_FUNCTION(Abbot, function25);
+ ADD_CALLBACK_FUNCTION(Abbot, function26);
+ ADD_CALLBACK_FUNCTION(Abbot, function27);
+ ADD_CALLBACK_FUNCTION(Abbot, function28);
+ ADD_CALLBACK_FUNCTION(Abbot, function29);
+ ADD_CALLBACK_FUNCTION(Abbot, function30);
+ ADD_CALLBACK_FUNCTION(Abbot, function31);
+ ADD_CALLBACK_FUNCTION(Abbot, function32);
+ ADD_CALLBACK_FUNCTION(Abbot, function33);
+ ADD_CALLBACK_FUNCTION(Abbot, function34);
+ ADD_CALLBACK_FUNCTION(Abbot, function35);
+ ADD_CALLBACK_FUNCTION(Abbot, function36);
+ ADD_CALLBACK_FUNCTION(Abbot, function37);
+ ADD_CALLBACK_FUNCTION(Abbot, function38);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4);
+ ADD_CALLBACK_FUNCTION(Abbot, function40);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function42);
+ ADD_CALLBACK_FUNCTION(Abbot, function43);
+ ADD_CALLBACK_FUNCTION(Abbot, function44);
+ ADD_CALLBACK_FUNCTION(Abbot, function45);
+ ADD_CALLBACK_FUNCTION(Abbot, function46);
+ ADD_CALLBACK_FUNCTION(Abbot, drinkAfterDefuse);
+ ADD_CALLBACK_FUNCTION(Abbot, function48);
+ ADD_CALLBACK_FUNCTION(Abbot, pickBomb);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5);
+ ADD_CALLBACK_FUNCTION(Abbot, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Abbot, function52);
+ ADD_CALLBACK_FUNCTION(Abbot, function53);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Abbot, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Abbot, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Abbot, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Abbot, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Abbot, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(6, Abbot, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(7, Abbot, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(8, Abbot, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Abbot, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Abbot, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(11, Abbot, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAbbot);
+ } else {
+ if (getEvent(kEventAbbotIntroduction))
+ getSound()->playSound(kEntityPlayer, "CAT1013");
+ else
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(12, Abbot, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Abbot, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Abbot, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Abbot, chapter1)
+ if (savepoint.action == kActionDefault)
+ getSavePoints()->addData(kEntityAbbot, kAction203073664, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Abbot, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAbbot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Abbot, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("804DD");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAbbot, kEntityCooks, kAction236976550);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(5);
+ setup_enterExitCompartment("617AC", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction192054567:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Abbot, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1953000) {
+ if (!params->param1) {
+ params->param1 = 1;
+ setCallback(3);
+ setup_playSound("MrB3010");
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setCallback(1);
+ setup_playSound("Abb3010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ setup_function20();
+ break;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Abbot, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel))
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "509A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Abbot, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("509B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("804US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAbbot, "029J");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029H");
+ getSavePoints()->push(kEntityAbbot, kEntityPascale, kAction207769280);
+ break;
+
+ case 7:
+ setup_function22();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_draw("029B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Abbot, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1971000, params->param1, kEntityAbbot, kEntityServers0, kAction218586752);
+
+ if (getState()->time > kTime1989000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function23();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotIntroduction);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ if (!getEvent(kEventAbbotIntroduction))
+ getData()->inventoryItem = (InventoryItem)kCursorProcess;
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getAction()->playAnimation(kEventAbbotIntroduction);
+ getSound()->playSound(kEntityPlayer, "LIB036");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Abbot, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction238936000);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Abbot, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ setup_function25();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.action == kActionKnock) {
+ setCallback(1);
+ setup_playSound("LIB012");
+ } else {
+ setCallback(2);
+ setup_playSound("LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Abb3001");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Abbot, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("617Dc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Abbot, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kAction101169422:
+ params->param1 = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Abbot, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115C", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Abbot, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param1, 1, setup_function29);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("abb3013");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction222609266:
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Abbot, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTicks(450);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(7);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Abbot, function30)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3030");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Abbot, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+ }
+ }
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param2 = (uint)getState()->time + 4500;
+ params->param3 = (uint)getState()->time + 18000;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("115E", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122358304);
+ getSound()->playSound(kEntityAbbot, "Abb3020");
+
+ setCallback(3);
+ setup_updatePosition("125A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125B");
+
+ setCallback(4);
+ setup_playSound("Abb3021");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAbbot, "Abb3023");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 52);
+
+ setCallback(5);
+ setup_draw2("125C1", "125C2", kEntityAlexei);
+ break;
+
+ case 5:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 52);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "125D");
+ getSavePoints()->push(kEntityAbbot, kEntityAlexei, kAction122288808);
+ params->param1 = 1;
+
+ UPDATE_PARAM(params->param5, getState()->time, 450);
+
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updatePosition("125E", kCarRestaurant, 52);
+ break;
+
+ case 7:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Abbot, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Abbot, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime2115000) {
+ if (getState()->time <= kTime2124000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 2000) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb3014");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+
+ case kAction123712592:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Abbot, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("Abb3031");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Bc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("115A", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getData()->location = kLocationInsideCompartment;
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Abbot, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param2)
+ params->param2 = (uint)getState()->time + 450;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityAbbot, kEntityAugust, kAction136196244);
+
+ setCallback(1);
+ setup_updateFromTime(0);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "115B");
+ params->param1 = (uint)getState()->time + 9000;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAbbot, "Abb3040", SoundManager::kFlagInvalid, 45);
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(3);
+ setup_callSavepoint("121A", kEntityAugust, kAction122358304, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function36();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Abbot, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ switch (params->param2) {
+ default:
+ break;
+
+ case 1:
+ if (params->param3 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time + 675;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSound()->playSound(kEntityAbbot, "Abb3041");
+ break;
+
+ case 2:
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ getSound()->playSound(kEntityAbbot, "Abb3042");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAbbot, "Abb3043");
+ getEntities()->updatePositionEnter(kEntityAbbot, kCarRestaurant, 57);
+
+ setCallback(1);
+ setup_callSavepoint("121D", kEntityAugust, kAction122288808, "BOGUS");
+ break;
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 4500;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "121B");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 57);
+ setup_function37();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Abbot, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("617Ac", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122358304);
+
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Abbot, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508A");
+
+ setCallback(1);
+ setup_playSound("Abb3014A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getEntities()->drawSequenceLeft(kEntityAbbot, "508B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Abbot, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(40, Abbot, function40, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAbbotInvitationDrink)
+ && getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotInvitationDrink);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAbbot, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAbbotInvitationDrink);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Abbot, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime2358000, params->param1, kEntityAbbot, kEntityServers0, kAction218128129);
+
+ if (getState()->time > kTime2389500 && getEntities()->isSomebodyInsideRestaurantOrSalon())
+ setup_function42();
+
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAbbot, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "029E");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAbbot, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Abbot, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_callSavepoint("029F", kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAbbot, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getEntities()->drawSequenceRight(kEntityAbbot, "804DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAbbot);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Abbot, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+ if (getState()->time < kTime2452500) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ } else {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityAbbot, kEntityPlayer, 1000) || getSound()->isBuffered(kEntityBoutarel) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Abb4002");
+ break;
+ }
+ }
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime2466000, params->param5, setup_function44);
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param2 = 1;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param3) {
+ setCallback(savepoint.param.intValue == 50 ? 5 : 6);
+ setup_playSound(savepoint.param.intValue == 50 ? getSound()->justAMinuteCath() : getSound()->wrongDoorCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("Abb3001");
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityAbbot, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param3 = 1;
+ break;
+
+ case 5:
+ case 6:
+ params->param2 = 1;
+ params->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ params->param1 = 1;
+ break;
+
+ case kAction159003408:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Abbot, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction104060776:
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Abbot, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38);
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec");
+ getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true);
+
+ setCallback(1);
+ setup_playSound("Abb4010");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("617Kc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAbbot, kObjectCompartmentC, true);
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Abbot, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6471;
+
+ setCallback(1);
+ setup_function40(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_drinkAfterDefuse();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Abbot, drinkAfterDefuse)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkGiveDetonator);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("126A");
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getData()->inventoryItem = kItemBomb;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAbbotDrinkGiveDetonator);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Abbot, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ UPDATE_PARAM_PROC(params->param1, getState()->time, 1800)
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_updatePosition("126C", kCarRedSleeping, 52);
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventAbbotDrinkDefuse);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityAbbot, kEntityVerges, kAction125233040);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("126A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ break;
+
+ case 4:
+ if (!getEvent(kEventAbbotDrinkDefuse) && ENTITY_PARAM(0, 1))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ params->param1 = 0;
+
+ TIME_CHECK_CALLBACK_INVENTORY(kTime2533500, params->param2, 5, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updatePosition("126D", kCarRestaurant, 52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+
+ setup_function44();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventAbbotDrinkDefuse);
+ getEntities()->drawSequenceLeft(kEntityAbbot, "126B");
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction100969180);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Abbot, pickBomb)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 150);
+
+ getSavePoints()->push(kEntityAbbot, kEntityAbbot, kAction157489665);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ case kAction157489665:
+ getSavePoints()->push(kEntityAbbot, kEntityTatiana, kAction238790488);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->call(kEntityAbbot, kEntityTables4, kActionDrawTablesWithChairs, "029G");
+ getSavePoints()->push(kEntityAbbot, kEntityServers0, kAction270068760);
+ getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction125039808);
+ getObjects()->update(kObjectCompartment2, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(getObjects()->get(kObjectCompartment2).location2 < kObjectLocation2 ? kEventAbbotWrongCompartmentBed : kEventAbbotWrongCompartment);
+ getEntities()->updateEntity(kEntityAbbot, kCarRedSleeping, kPosition_6470);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("617Cc", kObjectCompartmentC);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAbbot);
+ getObjects()->update(kObjectCompartmentC, kEntityAbbot, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Abbot, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Abbot, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Abbot, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAbbot);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+ break;
+
+ case kAction135600432:
+ setup_function53();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Abbot, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem25, kObjectLocation1);
+ getSavePoints()->push(kEntityAbbot, kEntityAnna, kAction158480160);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventLocomotiveAbbotGetSomeRest);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction168646401:
+ if (!getEvent(kEventLocomotiveAbbotGetSomeRest)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotGetSomeRest);
+ break;
+ }
+
+ if (!getEvent(kEventLocomotiveAbbotShoveling)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAbbotShoveling);
+ break;
+ }
+
+ getAction()->playAnimation(kEventLocomotiveAbbotShoveling);
+ getScenes()->processScene();
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h
new file mode 100644
index 0000000000..7e7b78b3be
--- /dev/null
+++ b/engines/lastexpress/entities/abbot.h
@@ -0,0 +1,225 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ABBOT_H
+#define LASTEXPRESS_ABBOT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Abbot : public Entity {
+public:
+ Abbot(LastExpressEngine *engine);
+ ~Abbot() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function40, CarIndex car, EntityPosition position)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(drinkAfterDefuse)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(pickBomb)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ABBOT_H
diff --git a/engines/lastexpress/entities/alexei.cpp b/engines/lastexpress/entities/alexei.cpp
new file mode 100644
index 0000000000..59b99fe968
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.cpp
@@ -0,0 +1,2002 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/alexei.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alexei::Alexei(LastExpressEngine *engine) : Entity(engine, kEntityAlexei) {
+ ADD_CALLBACK_FUNCTION(Alexei, reset);
+ ADD_CALLBACK_FUNCTION(Alexei, playSound);
+ ADD_CALLBACK_FUNCTION(Alexei, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Alexei, draw);
+ ADD_CALLBACK_FUNCTION(Alexei, updatePosition);
+ ADD_CALLBACK_FUNCTION(Alexei, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Alexei, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Alexei, savegame);
+ ADD_CALLBACK_FUNCTION(Alexei, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alexei, draw2);
+ ADD_CALLBACK_FUNCTION(Alexei, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Alexei, function13);
+ ADD_CALLBACK_FUNCTION(Alexei, function14);
+ ADD_CALLBACK_FUNCTION(Alexei, function15);
+ ADD_CALLBACK_FUNCTION(Alexei, function16);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function19);
+ ADD_CALLBACK_FUNCTION(Alexei, function20);
+ ADD_CALLBACK_FUNCTION(Alexei, function21);
+ ADD_CALLBACK_FUNCTION(Alexei, function22);
+ ADD_CALLBACK_FUNCTION(Alexei, function23);
+ ADD_CALLBACK_FUNCTION(Alexei, function24);
+ ADD_CALLBACK_FUNCTION(Alexei, function25);
+ ADD_CALLBACK_FUNCTION(Alexei, function26);
+ ADD_CALLBACK_FUNCTION(Alexei, function27);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function30);
+ ADD_CALLBACK_FUNCTION(Alexei, function31);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function34);
+ ADD_CALLBACK_FUNCTION(Alexei, function35);
+ ADD_CALLBACK_FUNCTION(Alexei, function36);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alexei, function39);
+ ADD_CALLBACK_FUNCTION(Alexei, function40);
+ ADD_CALLBACK_FUNCTION(Alexei, function41);
+ ADD_CALLBACK_FUNCTION(Alexei, function42);
+ ADD_CALLBACK_FUNCTION(Alexei, function43);
+ ADD_CALLBACK_FUNCTION(Alexei, function44);
+ ADD_CALLBACK_FUNCTION(Alexei, function45);
+ ADD_CALLBACK_FUNCTION(Alexei, function46);
+ ADD_CALLBACK_FUNCTION(Alexei, function47);
+ ADD_CALLBACK_FUNCTION(Alexei, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alexei, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Alexei, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(3, Alexei, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Alexei, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Alexei, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Alexei, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alexei, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(8, Alexei, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Alexei, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Alexei, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 18) || getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ getSound()->excuseMe(kEntityAlexei);
+ } else {
+ if (getEvent(kEventAlexeiSalonVassili) || (getEvent(kEventTatianaAskMatchSpeakRussian) && getInventory()->hasItem(kItemPassengerList))) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1012" : "CAT1012A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ }
+ // Stop execution here
+ return;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(11, Alexei, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alexei, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alexei, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2, true);
+ }
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction135664192:
+ setCallback(2);
+ setup_enterExitCompartment("602Eb", kObjectCompartment2);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alexei, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602Fb", kObjectCompartment2);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction302614416);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602DB");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ }
+ break;
+
+ case kAction135664192:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alexei, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(60) + 90);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(16, Alexei, function16, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7 && params->param1 < getState()->time && !params->param8) {
+ params->param8 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param5) {
+ if (savepoint.param.intValue == 18) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : "CAT1503");
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char*)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("ALX1134A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTicks(300);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("602Gb", kObjectCompartment2);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "602Hb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2, true);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2, true);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_7500;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, (char *)&params->seq);
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param7 = 0;
+ break;
+ }
+ break;
+
+ case kAction124697504:
+ setCallback(10);
+ setup_enterExitCompartment("602Ib", kObjectCompartment2);
+ break;
+
+ case kAction221617184:
+ params->param7 = 1;
+ getSavePoints()->push(kEntityAlexei, kEntityMertens, kAction100906246);
+
+ setCallback(7);
+ setup_playSound("CON1024");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alexei, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alexei, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ params->param2 = kItemNone;
+
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getInventory()->setLocationAndProcess(kItem17, kObjectLocation1);
+
+ setCallback(1);
+ setup_callSavepoint("005D", kEntityTables1, kActionDrawTablesWithChairs, "005E");
+ break;
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ params->param2 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiDiner);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "005B");
+
+ params->param2 = kItemInvalid;
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 63) ? 1 : 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function19();
+ break;
+
+ case 2:
+ getAction()->playAnimation(getProgress().jacket == kJacketGreen ? kEventAlexeiDiner : kEventAlexeiDinerOriginalJacket);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kActionDrawTablesWithChairs, "005E");
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityAlexei);
+ getInventory()->get(kItem17)->location = kObjectLocation1;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 63);
+
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ break;
+
+ case kAction225182640:
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Alexei, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("811DS");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_draw("933");
+ break;
+
+ case 6:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+ getScenes()->loadSceneFromItemPosition(kItem17);
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+
+ setCallback(7);
+ setup_callSavepoint("005F", kEntityTables1, kActionDrawTablesWithChairs, "005G");
+ break;
+
+ case 7:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityServers1, kAction302996448);
+
+ setCallback(8);
+ setup_draw("934");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_draw("811DS");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function13();
+ break;
+
+ case 10:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime1098000, "411");
+ break;
+
+ case 11:
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alexei, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alexei, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_CHECK(params->param2, getState()->time, params->param1)
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103C", kCarRestaurant, 52);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ params->param1 = 225 * (4 * rnd(3) + 4);
+
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function22();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alexei, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, params->param2)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updatePosition("103D", kCarRestaurant, 52);
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+ if (params->param3 == kTimeInvalid || getState()->time <= kTime1111500)
+ break;
+
+ if (getState()->time > kTime1138500) {
+ params->param3 = kTimeInvalid;
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+
+ params->param3 = kTimeInvalid;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ setup_function23();
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(4) + 8);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103E");
+ if (!getEvent(kEventAlexeiSalonPoem))
+ getData()->inventoryItem = kItemParchemin;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ setup_function21();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "103D");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 52);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 52);
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alexei, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (!getEntities()->isInRestaurant(kEntityAlexei) || getEvent(kEventAlexeiSalonPoem)) ? kItemNone : kItemParchemin;
+ break;
+
+ case kAction1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonPoem);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction124973510);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonVassili);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103F");
+ getScenes()->processScene();
+
+ setup_function24();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventAlexeiSalonPoem);
+
+ getData()->inventoryItem = kItemNone;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ break;
+ }
+ break;
+
+ case kAction157159392:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonVassili);
+ } else {
+ setup_function24();
+ }
+ break;
+
+ case kAction188784532:
+ setup_function24();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Alexei, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAlexeiSalonCath);
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventAlexeiSalonVassili))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAlexeiSalonCath);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_9460;
+ getEntities()->clearSequences(kEntityAlexei);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ setup_function25();
+ break;
+
+ case 2:
+ setup_function25();
+ break;
+ }
+ break;
+
+ case kAction135854208:
+ getData()->inventoryItem = kItemNone;
+ setCallback(2);
+ setup_draw("103G");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Alexei, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(2);
+ setup_function16(kTime1179000, "411");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTime1323000, "412");
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Alexei, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1512000, params->param1, setup_function27)
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->car = kCarGreenSleeping;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ getEntities()->clearSequences(kEntityAlexei);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Alexei, function27)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "412");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Alexei, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityAlexei, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Alexei, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime1791000, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("811US");
+ break;
+
+ case 5:
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(6);
+ setup_callSavepoint("018B", kEntityTables1, kAction136455232, "BOGUS");
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction290869168);
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Alexei, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->car = kCarRestaurant;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018C");
+ getSavePoints()->push(kEntityAlexei, kEntityTables1, kAction136455232);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction156444784);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "018E");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getProgress().field_68 = 1;
+
+ setCallback(2);
+ setup_playSound("TAT2116");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAlexei, "TAt2116A");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ setCallback(3);
+ setup_callSavepoint("018F", kEntityTatiana, kAction123857088, "BOGUS");
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAlexei, kCarRestaurant, 63);
+ setup_function31();
+ break;
+ }
+ break;
+
+ case kAction236053296:
+ getEntities()->drawSequenceRight(kEntityAlexei, "018D1");
+ getEntities()->drawSequenceRight(kEntityTatiana, "018D2");
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarRestaurant, 63);
+
+ if (savepoint.param.intValue)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, (Position)savepoint.param.intValue);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Alexei, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAlexei, "811DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAlexei);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function13();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(3);
+ setup_function16(kTimeEnd, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Alexei, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Alexei, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+
+ case kAction122288808:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Alexei, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2083500, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function35();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function13();
+ break;
+
+ case 7:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 61))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 66);
+
+ setCallback(8);
+ setup_function16(kTime2124000, "NONE");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function14();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function36();
+ break;
+
+ case 10:
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(11);
+ setup_function16(kTime16451100, "411");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Alexei, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 2700)
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ UPDATE_PARAM_PROC_END
+ } else {
+ params->param2 = 0;
+ }
+
+ UPDATE_PARAM_PROC(params->param3, getState()->time, params->param1)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(3);
+ setup_function15();
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+
+label_callback_3:
+ UPDATE_PARAM(params->param4, getState()->time, 9000);
+
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDefault:
+ params->param1 = 15 * rnd(120);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(getCallback() + 1);
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 2:
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ params->param1 = 15 * rnd(120);
+ params->param3 = 0;
+ goto label_callback_3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Alexei, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 || params->param2)
+ break;
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, params->param1);
+
+ getEntities()->drawSequenceRight(kEntityAlexei, "124B");
+
+ params->param2 = 1;
+ params->param4 = 0;
+ break;
+
+ case kActionExitCompartment:
+ if (params->param2) {
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ params->param1 = 5 * (3 * rnd(15) + 15);
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(15) + 15);
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAlexei, kEntityAbbot, kAction222609266);
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAlexei, "124A");
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setCallback(4);
+ setup_function13();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "BLANK");
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Alexei, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject10, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Alexei, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2354400, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function39();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Alexei, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2)
+ break;
+
+ if (!params->param4) {
+ params->param3 = (uint)getState()->time + 4500;
+ params->param4 = (uint)getState()->time + 9000;
+ }
+
+ if (params->param5 != kTimeInvalid && params->param3 < getState()->time) {
+
+ if (params->param4 >= getState()->time) {
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2 && !params->param2)
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("602FB", kObjectCompartment2);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ if (params->param1) {
+ if (!params->param2)
+ break;
+ } else if (!params->param2) {
+ getEntities()->drawSequenceRight(kEntityAlexei, "306A");
+ break;
+ }
+
+ setup_function40();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+ }
+ break;
+
+ case kAction123536024:
+ params->param2 = 1;
+ break;
+
+ case kAction123712592:
+ getEntities()->clearSequences(kEntityAlexei);
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Alexei, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityAlexei, "602Eb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartment2);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment2);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartment2);
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAlexei);
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Alexei, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(1);
+ setup_function16(kTime2403000, "411");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function42();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Alexei, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function14();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAlexei, kEntityTatiana, kAction191198209);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updatePosition("103A", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function43();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Alexei, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime1806300 && params->param2 < getState()->time) {
+ if (!params->param2)
+ params->param2 = (uint)getState()->time + params->param1;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setCallback(1);
+ setup_function15();
+ break;
+ }
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2457000 && !params->param3) {
+ params->param3 = 1;
+
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ getEntities()->drawSequenceLeft(kEntityAlexei, "103B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 5 * (3 * rnd(120) + 180);
+ params->param2 = 0;
+ goto label_callback_1;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("124C", kCarRestaurant, 52);
+ break;
+
+ case 3:
+ setup_function44();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Alexei, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2457000 && !params->param1) {
+ params->param1 = 1;
+
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityAlexei, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+
+ setup_function45();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_9460;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62)) {
+ setCallback(2);
+ setup_draw("306A");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityAlexei);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityAlexei, kCarGreenSleeping, 71);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAlexei, "306F");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Alexei, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ setup_function46();
+ } else {
+ setCallback(2);
+ setup_function16(kTimeEnd, "412");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Alexei, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime2493000) {
+
+ if (getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityAugust) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ getScenes()->loadSceneFromItemPosition(kItem22);
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityAlexei);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, getScenes()->get(getState()->scene)->position);
+ }
+
+ setCallback(4);
+ setup_function13();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2488500, "411");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 4:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 66))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(5);
+ setup_function16(kTime2507400, "412");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("602Fb", kObjectCompartment2);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 7:
+ getEntities()->drawSequenceRight(kEntityAlexei, "602Eb");
+ getEntities()->enterCompartment(kEntityAlexei, kObjectCompartmentB);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ }
+
+ setCallback(8);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 8:
+ getEntities()->exitCompartment(kEntityAlexei, kObjectCompartmentB);
+ getEntities()->clearSequences(kEntityAlexei);
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(9);
+ setup_playSound("TAT4167");
+ break;
+
+ case 9:
+ getSavePoints()->push(kEntityAlexei, kEntityChapters, kAction156435676);
+ setup_function47();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Alexei, function47)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityAlexei);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Alexei, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityAlexei);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alexei.h b/engines/lastexpress/entities/alexei.h
new file mode 100644
index 0000000000..420e6e87fc
--- /dev/null
+++ b/engines/lastexpress/entities/alexei.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ALEXEI_H
+#define LASTEXPRESS_ALEXEI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alexei : public Entity {
+public:
+ Alexei(LastExpressEngine *engine);
+ ~Alexei() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * ???
+ *
+ * @param timeValue The time value
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_2(function16, TimeValue timeValue, const char *sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+ DECLARE_FUNCTION(function43)
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALEXEI_H
diff --git a/engines/lastexpress/entities/alouan.cpp b/engines/lastexpress/entities/alouan.cpp
new file mode 100644
index 0000000000..2867fa726b
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.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 "lastexpress/entities/alouan.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Alouan::Alouan(LastExpressEngine *engine) : Entity(engine, kEntityAlouan) {
+ ADD_CALLBACK_FUNCTION(Alouan, reset);
+ ADD_CALLBACK_FUNCTION(Alouan, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Alouan, playSound);
+ ADD_CALLBACK_FUNCTION(Alouan, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Alouan, updateEntity);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Alouan, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function12);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function19);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5);
+ ADD_CALLBACK_FUNCTION(Alouan, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Alouan, function22);
+ ADD_CALLBACK_FUNCTION(Alouan, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Alouan, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Alouan, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Alouan, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Alouan, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Alouan, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Alouan, compartment6)
+ COMPARTMENT_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Cf", "621Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Alouan, compartment8)
+ COMPARTMENT_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Ch", "621Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Alouan, compartment6to8)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment6, kPosition_4070, "621Bf", kObjectCompartment8, kPosition_2740, "621Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Alouan, compartment8to6)
+ COMPARTMENT_FROM_TO(Alouan, kObjectCompartment8, kPosition_2740, "621Bh", kObjectCompartment6, kPosition_4070, "621Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Alouan, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Alouan, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+
+ TIME_CHECK_CALLBACK(kTime1096200, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (getState()->time > kTime1162800 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ getData()->entityPosition = kPosition_4070;
+ }
+
+ if (getState()->time > kTime1179000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(2);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Alouan, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Alouan, chapter2)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Alouan, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime1777500) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time + 75;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(params->param1 ? 1 : 2);
+ if (params->param1)
+ setup_compartment8();
+ else
+ setup_compartment6();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 3:
+ params->param1 = 0;
+ setCallback(4);
+ setup_playSound("Har2011");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(900);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAlouan, kEntityFrancois, kAction190219584);
+ break;
+ }
+ break;
+
+ case kAction189489753:
+ setCallback(3);
+ setup_compartment8to6();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Alouan, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Alouan, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeCitySalzbourg, params->param1, 1, setup_compartment8to6);
+
+label_callback1:
+ if (params->param2 != kTimeInvalid && getState()->time > kTime1989000)
+ TIME_CHECK_CAR(kTime2119500, params->param5, 5, setup_compartment8);
+
+label_callback2:
+ TIME_CHECK_CALLBACK_1(kTime2052000, params->param3, 3, setup_playSound, "Har1005");
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2133000, params->param4, 4, setup_compartment6to8);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2151000)
+ TIME_CHECK_CAR(kTime2241000, params->param5, 5, setup_compartment8);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4840;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Alouan, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Alouan, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime2443500, params->param1, 1, setup_compartment8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2455200, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ if (getState()->time > kTime2475000 && !params->param3) {
+ params->param3 = 1;
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+
+ setCallback(3);
+ setup_compartment6to8();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4070);
+ goto label_callback2;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Alouan, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAlouan);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Alouan, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Alouan, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Alouan, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Alouan, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment5);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAlouan);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Alouan)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/alouan.h b/engines/lastexpress/entities/alouan.h
new file mode 100644
index 0000000000..33d5e2f23d
--- /dev/null
+++ b/engines/lastexpress/entities/alouan.h
@@ -0,0 +1,139 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ALOUAN_H
+#define LASTEXPRESS_ALOUAN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Alouan : public Entity {
+public:
+ Alouan(LastExpressEngine *engine);
+ ~Alouan() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ALOUAN_H
diff --git a/engines/lastexpress/entities/anna.cpp b/engines/lastexpress/entities/anna.cpp
new file mode 100644
index 0000000000..e6752dad48
--- /dev/null
+++ b/engines/lastexpress/entities/anna.cpp
@@ -0,0 +1,4032 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/anna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Anna::Anna(LastExpressEngine *engine) : Entity(engine, kEntityAnna) {
+ ADD_CALLBACK_FUNCTION(Anna, reset);
+ ADD_CALLBACK_FUNCTION(Anna, draw);
+ ADD_CALLBACK_FUNCTION(Anna, updatePosition);
+ ADD_CALLBACK_FUNCTION(Anna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Anna, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Anna, playSound);
+ ADD_CALLBACK_FUNCTION(Anna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Anna, savegame);
+ ADD_CALLBACK_FUNCTION(Anna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Anna, function12);
+ ADD_CALLBACK_FUNCTION(Anna, draw2);
+ ADD_CALLBACK_FUNCTION(Anna, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Anna, function15);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1);
+ ADD_CALLBACK_FUNCTION(Anna, function17);
+ ADD_CALLBACK_FUNCTION(Anna, function18);
+ ADD_CALLBACK_FUNCTION(Anna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function20);
+ ADD_CALLBACK_FUNCTION(Anna, function21);
+ ADD_CALLBACK_FUNCTION(Anna, function22);
+ ADD_CALLBACK_FUNCTION(Anna, function23);
+ ADD_CALLBACK_FUNCTION(Anna, function24);
+ ADD_CALLBACK_FUNCTION(Anna, function25);
+ ADD_CALLBACK_FUNCTION(Anna, function26);
+ ADD_CALLBACK_FUNCTION(Anna, function27);
+ ADD_CALLBACK_FUNCTION(Anna, function28);
+ ADD_CALLBACK_FUNCTION(Anna, function29);
+ ADD_CALLBACK_FUNCTION(Anna, function30);
+ ADD_CALLBACK_FUNCTION(Anna, function31);
+ ADD_CALLBACK_FUNCTION(Anna, function32);
+ ADD_CALLBACK_FUNCTION(Anna, function33);
+ ADD_CALLBACK_FUNCTION(Anna, function34);
+ ADD_CALLBACK_FUNCTION(Anna, function35);
+ ADD_CALLBACK_FUNCTION(Anna, function36);
+ ADD_CALLBACK_FUNCTION(Anna, function37);
+ ADD_CALLBACK_FUNCTION(Anna, function38);
+ ADD_CALLBACK_FUNCTION(Anna, function39);
+ ADD_CALLBACK_FUNCTION(Anna, function40);
+ ADD_CALLBACK_FUNCTION(Anna, function41);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2);
+ ADD_CALLBACK_FUNCTION(Anna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3);
+ ADD_CALLBACK_FUNCTION(Anna, function45);
+ ADD_CALLBACK_FUNCTION(Anna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function47);
+ ADD_CALLBACK_FUNCTION(Anna, function48);
+ ADD_CALLBACK_FUNCTION(Anna, leaveTableWithAugust);
+ ADD_CALLBACK_FUNCTION(Anna, function50);
+ ADD_CALLBACK_FUNCTION(Anna, function51);
+ ADD_CALLBACK_FUNCTION(Anna, function52);
+ ADD_CALLBACK_FUNCTION(Anna, function53);
+ ADD_CALLBACK_FUNCTION(Anna, function54);
+ ADD_CALLBACK_FUNCTION(Anna, function55);
+ ADD_CALLBACK_FUNCTION(Anna, function56);
+ ADD_CALLBACK_FUNCTION(Anna, function57);
+ ADD_CALLBACK_FUNCTION(Anna, function58);
+ ADD_CALLBACK_FUNCTION(Anna, function59);
+ ADD_CALLBACK_FUNCTION(Anna, function60);
+ ADD_CALLBACK_FUNCTION(Anna, function61);
+ ADD_CALLBACK_FUNCTION(Anna, function62);
+ ADD_CALLBACK_FUNCTION(Anna, function63);
+ ADD_CALLBACK_FUNCTION(Anna, baggage);
+ ADD_CALLBACK_FUNCTION(Anna, function65);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4);
+ ADD_CALLBACK_FUNCTION(Anna, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function68);
+ ADD_CALLBACK_FUNCTION(Anna, function69);
+ ADD_CALLBACK_FUNCTION(Anna, function70);
+ ADD_CALLBACK_FUNCTION(Anna, function71);
+ ADD_CALLBACK_FUNCTION(Anna, function72);
+ ADD_CALLBACK_FUNCTION(Anna, function73);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5);
+ ADD_CALLBACK_FUNCTION(Anna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Anna, function76);
+ ADD_CALLBACK_FUNCTION(Anna, function77);
+ ADD_CALLBACK_FUNCTION(Anna, function78);
+ ADD_CALLBACK_FUNCTION(Anna, function79);
+ ADD_CALLBACK_FUNCTION(Anna, function80);
+ ADD_CALLBACK_FUNCTION(Anna, finalSequence);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Anna, reset)
+ Entity::reset(savepoint, true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Anna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Anna, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Anna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Anna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(6, Anna, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Anna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Anna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Anna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Anna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Anna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Anna, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2 && ENTITY_PARAM(0, 1))
+ params->param2 = 1;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, 75)
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+
+ params->param6 = 0;
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param4) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 0;
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ --params->param1;
+
+ getSavePoints()->push(kEntityAnna, kEntityAnna, kActionEndSound);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionEndSound:
+ if (params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ ++params->param1;
+
+ switch (params->param1) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, "ANN2135C");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityAnna, "ANN2135H");
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityAnna, "ANN2135K");
+ break;
+
+ case 9:
+ getSound()->playSound(kEntityAnna, "ANN2135I");
+ break;
+
+ case 10:
+ getSound()->playSound(kEntityAnna, "ANN2135J");
+ break;
+
+ case 11:
+ getSound()->playSound(kEntityAnna, "ANN2135M");
+ break;
+
+ case 12:
+ getSound()->playSound(kEntityAnna, "ANN2135L");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ if (savepoint.param.intValue == 53) {
+ getSound()->playSound(kEntityPlayer, getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ if (rnd(2)) {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ } else {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1506A" : "CAT1506");
+ }
+ } else {
+ getSound()->playSound(kEntityPlayer, getSound()->wrongDoorCath());
+ }
+
+ params->param4 = 0;
+ params->param5 = 0;
+ } else {
+ getSound()->removeFromQueue(kEntityAnna);
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->removeFromQueue(kEntityAnna);
+ setCallback(3);
+ setup_playSound("LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 49))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "418C");
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSound()->playSound(kEntityAnna, "ANN2135A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param5 || params->param4) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ ++params->param3;
+ if (params->param3 == 2) {
+ setCallback(2);
+ setup_draw("418B");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Ann1016");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorHand);
+ params->param4 = 1;
+ break;
+
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ --params->param1;
+ params->param6 = 1;
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAnna, "418A");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(13, Anna, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Anna, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Anna, function15, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param5 = 0;
+ params->param6 = 1;
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next action
+
+ case kActionKnock:
+ if (params->param5) {
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ if (savepoint.param.intValue == kObject53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ } else {
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ }
+ } else {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ if (params->param6 || params->param5) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param5 = 0;
+ params->param6 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param5 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ params->param5 = 0;
+ params->param6 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Anna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityAnna, kAction291662081, 0);
+ getSavePoints()->addData(kEntityAnna, kAction238936000, 1);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(17, Anna, function17, uint32, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->inventoryItem = (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAnna, kEntityPlayer, 2000)) ? (InventoryItem)LOW_BYTE(params->param3) : kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ if (savepoint.param.intValue == 8) {
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+ params->param3 &= 0xFFFFFFF7;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGiveScarf);
+ } else {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGotALight);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction) || getProgress().chapter >= kChapter2)
+ getSound()->playSound(kEntityPlayer, "CAT1001");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAnna);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventGotALight) && !getEvent(kEventGotALightD) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param3 = kItemInvalid;
+
+ if (!params->param3 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+ }
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf)
+ || getEvent(kEventAnnaGiveScarfDiner)
+ || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram)
+ || getEvent(kEventAnnaGiveScarfDinerMonogram)
+ || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ getAction()->playAnimation(kEventAnnaGiveScarfAsk);
+ else if (getEvent(kEventAugustPresentAnna)
+ || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ getAction()->playAnimation(kEventAnnaGiveScarfMonogram);
+ else
+ getAction()->playAnimation(kEventAnnaGiveScarf);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->direction == kDirectionUp ? kEventGotALightD : kEventGotALight);
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+ params->param3 &= 0xFFFFFF7F;
+
+ if (getProgress().jacket == kJacketGreen && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param3 |= 8;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Anna, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && params->param1 < getState()->time && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param5 && !params->param4) {
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 900)
+ params->param2 |= kItemScarf;
+ params->param5 = 0;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kAction1:
+ setCallback(savepoint.param.intValue == 8 ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, savepoint.param.intValue == 8 ? kEventAnnaGiveScarf : kEventDinerMindJoin);
+ break;
+
+ case kActionDefault:
+ if (getProgress().jacket == kJacketGreen) {
+ if (!getEvent(kEventDinerMindJoin) && !getEvent(kEventAugustPresentAnna) && !getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ params->param2 |= kItemInvalid;
+
+ if (!params->param2 && !getEvent(kEventAnnaGiveScarfAsk) && !getEvent(kEventAnnaGiveScarfDinerAsk) && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ break;
+
+ case kActionDrawScene:
+ params->param3 = getEntities()->isPlayerPosition(kCarRestaurant, 62);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram)) {
+ getAction()->playAnimation(kEventAnnaGiveScarfDinerAsk);
+ } else {
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaGiveScarfDinerMonogram : kEventAnnaGiveScarfDiner);
+ params->param5 = 1;
+ }
+
+ params->param2 &= 0xFFFFFFF7;
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventDinerMindJoin);
+
+ params->param2 &= 0xFFFFFFF7;
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk)) {
+ params->param2 |= 8;
+ }
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ params->param4 = 1;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param2);
+ params->param4 = 0;
+ break;
+
+ case kAction170016384:
+ case kAction259136835:
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Anna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("618Ca", kObjectCompartment1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8514;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Anna, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function15(kTime1093500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Anna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("801US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityAnna, "001B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Anna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001A");
+ getSavePoints()->push(kEntityAnna, kEntityPascale, kAction223262556);
+ break;
+
+ case kAction157370960:
+ getData()->location = kLocationInsideCompartment;
+ setup_function23();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Anna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001D");
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction270410280);
+ getSavePoints()->push(kEntityAnna, kEntityTables0, kAction136455232);
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001E");
+ setCallback(2);
+ setup_playSound("ANN1048");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_draw("001F");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction203859488);
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Anna, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001G");
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001H");
+ setCallback(2);
+ setup_playSound("ANN1049");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction136702400);
+ setup_function25();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Anna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ getProgress().field_28 = 1;
+
+ setCallback(1);
+ setup_function18(kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setup_function26();
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+
+ case kAction201437056:
+ getEntities()->drawSequenceLeft(kEntityAnna, "001J");
+ setCallback(2);
+ setup_function18(kTime1138500);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Anna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+
+ setCallback(1);
+ setup_callSavepoint("001L", kEntityTables0, kActionDrawTablesWithChairs, "001H");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 62);
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction237485916);
+ getEntities()->drawSequenceRight(kEntityAnna, "801DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Anna, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ setCallback(1);
+ setup_function15(kTime1156500, "NONE");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (getProgress().field_14 == 29) {
+ params->param1 = (uint)(getState()->time + 900);
+ setCallback(2);
+ setup_function15((TimeValue)params->param1, "NONE");
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("618Bf", kObjectCompartmentF);
+ }
+ break;
+
+ case 3:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Anna, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_1540;
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ setCallback(3);
+ setup_updatePosition("104A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Anna, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 900)
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+ params->param2 = 0;
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param4 = 0;
+ }
+ break;
+
+ case kAction1:
+ setCallback(savepoint.param.intValue == 8 ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, savepoint.param.intValue == 8 ? kEventAnnaGiveScarf : kEventAnnaIntroductionRejected);
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaConversationGoodNight)
+ && !getEvent(kEventAnnaIntroductionRejected))
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getProgress().jacket == kJacketGreen
+ && getData()->inventoryItem == kItemNone
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "104B");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 56);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAnnaGiveScarf)
+ || getEvent(kEventAnnaGiveScarfDiner)
+ || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram)
+ || getEvent(kEventAnnaGiveScarfDinerMonogram)
+ || getEvent(kEventAnnaGiveScarfSalonMonogram)) {
+ getAction()->playAnimation(kEventAnnaGiveScarfSalonAsk);
+ } else {
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaGiveScarfSalonMonogram : kEventAnnaGiveScarfSalon);
+ params->param2 = 1;
+ }
+
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+
+ case 2:
+ getAction()->playAnimation((getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction)) ? kEventAnnaConversationGoodNight : kEventAnnaIntroductionRejected);
+
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleLow);
+
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventAnnaGiveScarfAsk)
+ && !getEvent(kEventAnnaGiveScarfDinerAsk)
+ && !getEvent(kEventAnnaGiveScarfSalonAsk))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemScarf);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getData()->inventoryItem = kItemNone;
+ setup_function30();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Anna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid && getState()->time) {
+ if (getState()->time > kTime1188000) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ } else {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)(getState()->time + 450);
+
+ if (params->param3 < getState()->time) {
+ params->param3 = kTimeInvalid;
+ getSound()->playSound(kEntityAnna, "AUG1004");
+ }
+ }
+ }
+
+ if (params->param2 && params->param4 != kTimeInvalid && getState()->time > kTime1179000) {
+
+ if (getState()->time > kTime1192500) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 150);
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ setup_function30();
+ break;
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 1;
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAnna, "106B");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 56);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Anna, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityAnna, "AUG1005");
+
+ setCallback(2);
+ setup_updateFromTicks(150);
+ break;
+
+ case 2:
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 56);
+
+ setCallback(3);
+ setup_draw2("106C1", "106C2", kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 56);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction159332865);
+
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Anna, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("618Af", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function33();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Anna, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+
+ params->param1 = (uint)(getState()->time + 4500);
+ setCallback(1);
+ setup_function15((TimeValue)params->param1, "NONE");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->updateLocation2(kObjectCompartmentF, kObjectLocation1);
+ setup_function34();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Anna, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarRedSleeping, 60)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 150)
+ setCallback(1);
+ setup_draw("419B");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_1:
+ TIME_CHECK(kTime1489500, params->param3, setup_function35);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ case 3:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(4);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Anna, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ switch (params->param2) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityAnna, "ANN2135E");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityAnna, "ANN2135F");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityAnna, "ANN2135G");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityAnna, "ANN2135D");
+ break;
+ }
+
+ params->param1 = 0;
+ params->param3 = 0;
+ break;
+
+ case kActionEndSound:
+ ++params->param2;
+
+ if (params->param2 > 3)
+ params->param2 = 0;
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaVisitToCompartmentGun);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+ params->param1 = 1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaVisitToCompartmentGun);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+
+ getEntities()->updateEntity(kEntityAnna, kCarRedSleeping, kPosition_8200);
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityVassili, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityVerges, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction339669520);
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setup_function36();
+ break;
+
+ case 2:
+ setup_function36();
+ break;
+ }
+ break;
+
+ case kAction226031488:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+ break;
+
+ case kAction238358920:
+ setCallback(2);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Anna, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("608Aa", kObjectCompartmentA);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Anna, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Anna, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+
+ setCallback(1);
+ setup_playSound("ANN1010");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->playSound(kEntityPlayer, "MUS043");
+ setup_function40();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(39, Anna, function39, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaGoodNight);
+ break;
+
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityAnna, "ANN1107A");
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ if (!getEvent(kEventAnnaGoodNight) && !getEvent(kEventAnnaGoodNightInverse))
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getData()->direction == kDirectionNone ? kEventAnnaGoodNight : kEventAnnaGoodNightInverse);
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Anna, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(4);
+ setup_updateFromTime(150);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("608Cf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_function39(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("608Bb", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(8);
+ setup_updateFromTime(150);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("608Cb", kObjectCompartmentB);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function39(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("608Bf", kObjectCompartmentF);
+ break;
+
+ case 11:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4070;
+
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Anna, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ params->param5++;
+ switch (params->param5) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ params->param1 = 0;
+ break;
+ }
+
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "419C");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(3);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Anna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Anna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function15(kTime1786500, "418C");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function15(kTime1818000, "418C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function12();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function15(kTimeEnd, "418C");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Anna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(45, Anna, function45, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("625Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, params->param1 ? kAction185737168 : kAction185671840);
+ getSound()->playSound(kEntityAnna, "Ann3147");
+ getEntities()->drawSequenceLeft(kEntityAnna, "625EF");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction157894320:
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Anna, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function47();
+ } else {
+ setCallback(2);
+ setup_function15((TimeValue)(getState()->time + 4500), "418C");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Anna, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("688Bf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction71277948);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("801VS");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityAnna, getEvent(kEventAugustLunch) ? "Ann3136" : "Ann3136A", SoundManager::kFlagInvalid, 30);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122358304);
+
+ setCallback(5);
+ setup_draw2("026B1", "026B2", kEntityAugust);
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ setup_function48();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Anna, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (params->param3 != kTimeInvalid && getState()->time > kTime1969200) {
+ UPDATE_PARAM_PROC_TIME(kTime1983600, (!getEntities()->isInRestaurant(kEntityPlayer) || getSound()->isBuffered(kEntityBoutarel)), params->param3, 150)
+ setCallback(3);
+ setup_playSound("Aug3007A");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 2)) {
+ if (!params->param2)
+ params->param2 = (uint)(getState()->time + 4500);
+
+ if (params->param4 != kTimeInvalid) {
+ if (params->param2 >= getState()->time) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 450);
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setup_function50();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026C");
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Ann3137B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityServers0, kAction218983616);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("Aug3006A");
+ break;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound("Aug3006");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(2700);
+ break;
+
+ case 8:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026H");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "026C");
+
+ setCallback(5);
+ setup_playSound("Ann3138A");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Anna, leaveTableWithAugust)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityAnna, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getEntities()->clearSequences(kEntityAugust);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables3, "026J3");
+ getEntities()->drawSequenceRight(kEntityAugust, "026J2");
+ getEntities()->drawSequenceRight(kEntityAnna, "026J1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Anna, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("ann3141");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(3);
+ setup_leaveTableWithAugust();
+ break;
+
+ case 3:
+ setup_function51();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Anna, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getSound()->playSound(kEntityAnna, "Aug3008");
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw2("112E1", "112E2", kEntityAugust);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityAnna, "Aug3142", SoundManager::kFlagInvalid, 30);
+ getEntities()->updatePositionEnter(kEntityAnna, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAnna, "112A");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "112B");
+ getEntities()->updatePositionExit(kEntityAnna, kCarRestaurant, 57);
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction219377792);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function52();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(1800);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("Aug3007");
+ break;
+
+ case 6:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101169422:
+ if (getEvent(kEventKronosVisit)) {
+ setCallback(3);
+ setup_updatePosition("112J", kCarRestaurant, 57);
+ break;
+ }
+
+ if (getState()->time >= kTimeEnterAttnangPuchheim) {
+ params->param1 = 1;
+ } else {
+ setCallback(4);
+ setup_playSound("Ann3142A");
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "112D");
+ getSavePoints()->push(kEntityAnna, kEntityKronos, kAction157159392);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Anna, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityAnna);
+
+ setup_function53();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceRight(kEntityAnna, "688Af");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF);
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Anna, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_48 && params->param5 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTime2065500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param5, 150)
+ setup_function54();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 9000)
+ params->param4 = !params->param4;
+ getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A");
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 75);
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param7 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction101687594);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->clothes = kClothes2;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+
+ if (!params->param3 && (getEntities()->isPlayerPosition(kCarRedSleeping, 60) || getState()->time > kTime2034000)) {
+ params->param3 = 1;
+
+ setCallback(9);
+ setup_draw("416");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ }
+
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 9:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+ getEntities()->drawSequenceLeft(kEntityAnna, "417B");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Anna, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3) {
+ TIME_CHECK(kTime2079000, params->param5, setup_function55);
+
+ UPDATE_PARAM_PROC(params->param6, getState()->time, 9000)
+ params->param4 = !params->param4;
+ getEntities()->drawSequenceLeft(kEntityAnna, params->param4 ? "417B" : "417A");
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 75);
+
+ CursorStyle cursor = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, cursor);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param7 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB013");
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(6);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction189750912);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ setCallback(2);
+ setup_playSound("MAX1120");
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("ANN1016");
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 6:
+ case 7:
+ case 8:
+ if (getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070)) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorHand);
+ }
+
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4070;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ getEntities()->drawSequenceLeft(kEntityAnna, "417B");
+ break;
+ }
+ break;
+
+ case kAction123733488:
+ setCallback(9);
+ setup_enterExitCompartment("629Ef", kObjectCompartmentF);
+ break;
+
+ case kAction156049968:
+ getEntities()->drawSequenceLeft(kEntityAnna, "629DF");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case kAction253868128:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Anna, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemKey, kObjectLocation1);
+
+ setCallback(1);
+ setup_function45(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Anna, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function57();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Anna, function57)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction191668032);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction205033696);
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Ef");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAnna, "625Gf");
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction169032608);
+ break;
+
+ case 4:
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityAnna, "Aug3009A");
+
+ setCallback(6);
+ setup_enterExitCompartment("628Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+
+ setup_function59();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAnna, "628Af");
+
+ if (getSound()->isBuffered(kEntityAugust)) {
+ setCallback(4);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(5);
+ setup_playSound("Aug3009");
+ }
+ break;
+
+ case kAction192063264:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF, true);
+ setup_function58();
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("625Ff", kObjectCompartmentF);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Anna, function58)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaSearchingCompartment);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaSearchingCompartment);
+ getEntities()->clearSequences(kEntityAnna);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ getSound()->playSound(kEntityAnna, "lib015");
+ getSavePoints()->push(kEntityAnna, kEntityAugust, kAction122288808);
+ setup_function59();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Anna, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getInventory()->hasItem(kItemKey) && params->param4 != kTimeInvalid && getState()->time > kTime2218500) {
+ if (getState()->time > kTime2248200) {
+ params->param4 = kTimeInvalid;
+ setup_function61();
+ break;
+ }
+
+ if (!params->param3
+ || (!getEntities()->isPlayerInCar(kCarRedSleeping)
+ && !getEntities()->isInSalon(kEntityPlayer)
+ && !getEntities()->isInRestaurant(kEntityPlayer))
+ || !params->param4)
+ params->param4 = (uint)getState()->time;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ setup_function61();
+ break;
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ CursorStyle style = getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, style);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, style);
+
+ params->param1= 0;
+ params->param2 = 1;
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (savepoint.param.intValue == 53) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1506" : "CAT1506A"));
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 60))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 78);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("ANN1016");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction100906246);
+ break;
+ }
+ break;
+
+ case kAction156622016:
+ if (params->param3) {
+ setCallback(8);
+ setup_function60();
+ }
+ break;
+
+ case kAction236241630:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_playSound("Ann1016A");
+ break;
+
+ case kAction236517970:
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Anna, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityMax, kAction122358304);
+ getSound()->playSound(kEntityAnna, rnd(2) ? "Ann3126" : "Ann3127");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Cf", kObjectCompartmentF);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("630Df", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAnna);
+ getSavePoints()->push(kEntityAnna, kEntityCoudert, kAction189026624);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction156049968:
+ setCallback(3);
+ setup_enterExitCompartment("629EF", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Anna, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 3;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeIndex, 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function45(false);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_draw("802US");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceRight(kEntityAnna, "802UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAnna);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Anna, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2259000 && !params->param2) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityAnna, kEntityVesna, kAction189299008);
+ setup_function63();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getProgress().field_54 = 1;
+ break;
+
+ case kAction235856512:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, Anna, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction171843264);
+ break;
+
+ // Game over with Anna killed!
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaKilled);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ }
+ break;
+
+ // Anna will get killed...
+ case kAction272177921:
+ if (getSound()->isBuffered("MUS012"))
+ getSound()->processEntry("MUS012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKilled);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, Anna, baggage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageArgument);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaBaggageArgument);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, (EventIndex)kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightAnna);
+
+ if (params->param1)
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ else {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBagagePart2);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAnnaBagagePart2);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 96);
+
+ getProgress().field_54 = 0;
+ getState()->time = kTime2266200;
+
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, Anna, function65)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_function15(kTimeEnd, "NONE");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, Anna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, Anna, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 46)) {
+ UPDATE_PARAM_GOTO(params->param4, getState()->timeTicks, 30, label_next);
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+ }
+
+ params->param4 = 0;
+
+label_next:
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, getEntities()->isInsideCompartment(kEntityMax, kCarRedSleeping, kPosition_4070) ? kCursorHand : kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaConversation_34);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityAnna, "511B");
+ break;
+
+ case kActionDrawScene:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAnnaConversation_34);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 8);
+
+ setup_function68();
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_playSound("ANN1016");
+ break;
+
+ case 4:
+ getObjects()->update(kObject53, kEntityAnna, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 5:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->inventoryItem = kItemNone;
+
+ setup_function69();
+ break;
+
+ case kAction219971920:
+ params->param3 = 1;
+ getData()->inventoryItem = kItemInvalid;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, Anna, function68)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ setCallback(1);
+ setup_function15(kTime2511900, "NONE");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kAction191001984:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function69();
+ break;
+
+ case kAction201431954:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, Anna, function69)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ break;
+ }
+
+ TIME_CHECK_CALLBACK(kTime2535300, params->param3, 4, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ setup_function70();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updatePosition("127A", kCarRestaurant, 56);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAnna, "127B");
+ getSavePoints()->push(kEntityAnna, kEntityServers1, kAction258136010);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("127G", kCarRestaurant, 56);
+ break;
+
+ case 5:
+ setup_function70();
+ break;
+ }
+ break;
+
+ case kAction100969180:
+ getEntities()->clearSequences(kEntityAnna);
+ params->param1 = 1;
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAnna, "127E");
+ getSavePoints()->push(kEntityAnna, kEntityAbbot, kAction203073664);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAnna, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(70, Anna, function70)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function72(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function71();
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->clearSequences(kEntityAnna);
+ setup_function73();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(71, Anna, function71)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityAnna, kObjectCompartmentF);
+ getData()->entityPosition = kPosition_4070;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityAnna, "625Af");
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 7)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 28)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 56))
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+
+ getEntities()->enterCompartment(kEntityAnna, kObjectCompartmentF);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4070)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentF, true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventAnnaTiredKiss)
+ && getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityAnna, 2000)
+ && getEntities()->hasValidFrame(kEntityAnna)
+ && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTiredKiss);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTiredKiss);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 29);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(72, Anna, function72, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaTired) || getEntities()->isWalkingOppositeToPlayer(kEntityAnna))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaTired);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAnna, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (!getEvent(kEventAnnaTired))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAnnaTired);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(73, Anna, function73)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (params->param2 >= getState()->time) {
+ if (!((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) && params->param3))
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainHijacked);
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaKissTrainHijacked);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getState()->timeDelta = 1;
+
+ params->param1 = (uint)(getState()->time + 4500);
+ params->param2 = (uint)(getState()->time + 9000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Ann4200");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityAnna, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAnnaKissTrainHijacked);
+ getSavePoints()->push(kEntityAnna, kEntityChapters, kAction139254416);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(74, Anna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAnna);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->clothes = kClothes3;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectOutsideAnnaCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(75, Anna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getProgress().field_C)
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies2 : kEventAnnaBaggageTies);
+ else
+ getAction()->playAnimation(getEvent(kEventAnnaKissTrainHijacked) ? kEventAnnaBaggageTies3 : kEventAnnaBaggageTies4);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 8);
+ setup_function76();
+ }
+ break;
+
+ case kAction272177921:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaBaggageTies);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(76, Anna, function76)
+ if (savepoint.action == kAction158480160)
+ setup_function77();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(77, Anna, function77)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime3645000 && !params->param2) {
+ params->param2 = 1;
+ getState()->timeDelta = 0;
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB014");
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventAnnaDialogGoToJerusalem);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject106, kEntityAnna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getObjects()->update(kObject106, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getAction()->playAnimation(kEventAnnaDialogGoToJerusalem);
+
+ getState()->time = kTimeCityConstantinople;
+ getState()->timeDelta = 0;
+
+ getSavePoints()->push(kEntityAnna, kEntityTatiana, kAction236060709);
+
+ getScenes()->loadSceneFromPosition(kCarBaggage, 97, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 3:
+ setup_function78();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(78, Anna, function78)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDrawScene:
+ if ((getEntities()->isInRestaurant(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer)) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ getState()->time = kTimeInvalid2;
+
+ setCallback(getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? 2 : 1);
+ setup_savegame(kSavegameTypeEvent, getInventory()->get(kItemFirebird)->location == kObjectLocation4 ? kEventKronosHostageAnna : kEventKronosHostageAnnaNoFirebird);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosHostageAnnaNoFirebird);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosHostageAnna);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ getSound()->playSound(kEntityAnna, "Mus024", SoundManager::kFlagDefault);
+ setup_function79();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(79, Anna, function79)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getState()->time = kTime5933;
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && getInventory()->hasItem(kItemFirebird)) {
+ setup_function80();
+ break;
+ }
+
+ if (getEntities()->isInSalon(kEntityPlayer) && !getEvent(kEventKahinaPunch)) {
+ getState()->time = kTime5933;
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchRestaurant);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchKitchen);
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCarEntrance);
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ getAction()->playAnimation(kEventKahinaPunchBaggageCar);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunchSalon);
+ break;
+ }
+
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(80, Anna, function80)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 450);
+
+ getSound()->playSound(kEntityPlayer, "Kro5001", SoundManager::kFlagDefault);
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityPlayer, "Kro5002", SoundManager::kFlagDefault);
+ getState()->time = kTime4923000;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringFirebird);
+ break;
+
+ case kActionDefault:
+ getState()->time = kTime4929300;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunch);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosBringFirebird);
+ getScenes()->loadSceneFromItem(kItemFirebird);
+ getSound()->playSound(kEntityAnna, "Mus025", SoundManager::kFlagDefault);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKahinaPunch);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getProgress().isEggOpen = true;
+
+ if (getSound()->isBuffered(kEntityAnna))
+ getSound()->processEntry(kEntityAnna);
+
+ getAction()->playAnimation(kEventKronosOpenFirebird);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 3);
+
+ setup_finalSequence();
+ break;
+ }
+ break;
+
+ case kAction205294778:
+ getState()->time = kTime4929300;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosOpenFirebird);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(81, Anna, finalSequence)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->timeTicks, 180);
+
+ getSound()->playSound(kEntityTrain, "LIB069");
+ getLogic()->gameOver(kSavegameTypeIndex, 2, kSceneNone, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCathCloseEggNoBackground);
+ getAction()->playAnimation(kEventKronosGiveFirebird);
+
+ if (getInventory()->hasItem(kItemWhistle))
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverTrainExplosion, true);
+ else if (getInventory()->get(kItemWhistle)->location == kObjectLocation1)
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAnnaDialogGoToJerusalem, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventAugustUnhookCarsBetrayal, kSceneGameOverTrainExplosion2, true);
+ break;
+
+ case 2:
+ getInventory()->removeItem(kItemWhistle);
+ getLogic()->playFinalSequence();
+ break;
+ }
+ break;
+
+ case kAction224309120:
+ getProgress().isEggOpen = false;
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGiveFirebird);
+ break;
+
+ case kActionUseWhistle:
+ getProgress().isEggOpen = false;
+ setGlobalTimer(0);
+ getState()->time = kTimeCityConstantinople;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventFinalSequence);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/anna.h b/engines/lastexpress/entities/anna.h
new file mode 100644
index 0000000000..5297d6f745
--- /dev/null
+++ b/engines/lastexpress/entities/anna.h
@@ -0,0 +1,252 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ANNA_H
+#define LASTEXPRESS_ANNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Anna : public Entity {
+public:
+ Anna(LastExpressEngine *engine);
+ ~Anna() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_2(function15, TimeValue timeValue, const char *sequence)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_2(function17, uint32, uint32)
+
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION_2(function39, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+ DECLARE_FUNCTION_1(function45, bool useAction1)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(leaveTableWithAugust)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+ DECLARE_FUNCTION(function57)
+ DECLARE_FUNCTION(function58)
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(baggage)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(function69)
+ DECLARE_FUNCTION(function70)
+ DECLARE_FUNCTION(function71)
+ DECLARE_FUNCTION_2(function72, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION(function73)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+ DECLARE_FUNCTION(function76)
+ DECLARE_FUNCTION(function77)
+ DECLARE_FUNCTION(function78)
+ DECLARE_FUNCTION(function79)
+ DECLARE_FUNCTION(function80)
+ DECLARE_FUNCTION(finalSequence)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ANNA_H
diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp
new file mode 100644
index 0000000000..36d8ed4fe4
--- /dev/null
+++ b/engines/lastexpress/entities/august.cpp
@@ -0,0 +1,3536 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/august.h"
+
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+August::August(LastExpressEngine *engine) : Entity(engine, kEntityAugust) {
+ ADD_CALLBACK_FUNCTION(August, reset);
+ ADD_CALLBACK_FUNCTION(August, updateFromTime);
+ ADD_CALLBACK_FUNCTION(August, draw);
+ ADD_CALLBACK_FUNCTION(August, updatePosition);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(August, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(August, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(August, callSavepoint);
+ ADD_CALLBACK_FUNCTION(August, callSavepointNoDrawing);
+ ADD_CALLBACK_FUNCTION(August, draw2);
+ ADD_CALLBACK_FUNCTION(August, playSound);
+ ADD_CALLBACK_FUNCTION(August, playSound16);
+ ADD_CALLBACK_FUNCTION(August, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(August, savegame);
+ ADD_CALLBACK_FUNCTION(August, updateEntity);
+ ADD_CALLBACK_FUNCTION(August, function17);
+ ADD_CALLBACK_FUNCTION(August, updateEntity2);
+ ADD_CALLBACK_FUNCTION(August, function19);
+ ADD_CALLBACK_FUNCTION(August, function20);
+ ADD_CALLBACK_FUNCTION(August, function21);
+ ADD_CALLBACK_FUNCTION(August, chapter1);
+ ADD_CALLBACK_FUNCTION(August, function23);
+ ADD_CALLBACK_FUNCTION(August, dinner);
+ ADD_CALLBACK_FUNCTION(August, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(August, function26);
+ ADD_CALLBACK_FUNCTION(August, function27);
+ ADD_CALLBACK_FUNCTION(August, function28);
+ ADD_CALLBACK_FUNCTION(August, function29);
+ ADD_CALLBACK_FUNCTION(August, restaurant);
+ ADD_CALLBACK_FUNCTION(August, function31);
+ ADD_CALLBACK_FUNCTION(August, function32);
+ ADD_CALLBACK_FUNCTION(August, function33);
+ ADD_CALLBACK_FUNCTION(August, function34);
+ ADD_CALLBACK_FUNCTION(August, chapter2);
+ ADD_CALLBACK_FUNCTION(August, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(August, function37);
+ ADD_CALLBACK_FUNCTION(August, function38);
+ ADD_CALLBACK_FUNCTION(August, function39);
+ ADD_CALLBACK_FUNCTION(August, chapter3);
+ ADD_CALLBACK_FUNCTION(August, function41);
+ ADD_CALLBACK_FUNCTION(August, function42);
+ ADD_CALLBACK_FUNCTION(August, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(August, function44);
+ ADD_CALLBACK_FUNCTION(August, function45);
+ ADD_CALLBACK_FUNCTION(August, function46);
+ ADD_CALLBACK_FUNCTION(August, function47);
+ ADD_CALLBACK_FUNCTION(August, function48);
+ ADD_CALLBACK_FUNCTION(August, function49);
+ ADD_CALLBACK_FUNCTION(August, function50);
+ ADD_CALLBACK_FUNCTION(August, function51);
+ ADD_CALLBACK_FUNCTION(August, function52);
+ ADD_CALLBACK_FUNCTION(August, function53);
+ ADD_CALLBACK_FUNCTION(August, function54);
+ ADD_CALLBACK_FUNCTION(August, function55);
+ ADD_CALLBACK_FUNCTION(August, function56);
+ ADD_CALLBACK_FUNCTION(August, chapter4);
+ ADD_CALLBACK_FUNCTION(August, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(August, function59);
+ ADD_CALLBACK_FUNCTION(August, function60);
+ ADD_CALLBACK_FUNCTION(August, function61);
+ ADD_CALLBACK_FUNCTION(August, function62);
+ ADD_CALLBACK_FUNCTION(August, function63);
+ ADD_CALLBACK_FUNCTION(August, function64);
+ ADD_CALLBACK_FUNCTION(August, function65);
+ ADD_CALLBACK_FUNCTION(August, chapter5);
+ ADD_CALLBACK_FUNCTION(August, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(August, function68);
+ ADD_CALLBACK_FUNCTION(August, unhookCars);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, August, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, August, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, August, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, August, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, August, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, August, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarGreenSleeping, kObjectCompartment3, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, August, enterExitCompartment3, ObjectIndex)
+ if (savepoint.action == kAction4) {
+ getEntities()->exitCompartment(kEntityAugust, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, August, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(9, August, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, August, callSavepointNoDrawing, EntityIndex, ActionIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param6)
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ if (!params->param6) {
+ getSavePoints()->call(kEntityAugust, (EntityIndex)params->param1, (ActionIndex)params->param2, (char *)&params->seq);
+ params->param6 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(11, August, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(12, August, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(13, August, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, August, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(15, August, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(16, August, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getProgress().eventMetAugust ? getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002A" : "CAT1002") : getSound()->excuseMeCath();
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, August, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ params->param2 = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_updateEntity2(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_updateEntity2(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(18, August, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, August, function19, bool, bool)
+ // Expose parameters as IISS and ignore the default exposed parameters
+ EntityData::EntityParametersIISS *parameters = (EntityData::EntityParametersIISS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, "AUG3101", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ strcpy((char *)&parameters->seq1, "626");
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (getData()->clothes != kClothes2) {
+ strcpy((char *)&parameters->seq1, "666");
+ break;
+ }
+ // Fallback to next action
+
+ case kChapter4:
+ case kChapter5:
+ strcpy((char *)&parameters->seq1, "696");
+ break;
+ }
+
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction303343617);
+
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, "Pc");
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ setCallback(1);
+ setup_playSound("AUG2096");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, "Qc");
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ if (parameters->param2)
+ getData()->inventoryItem = kItem147;
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+ strcat((char *)&parameters->seq2, parameters->param1 ? "Fc" : "Dc");
+
+ setCallback(3);
+ setup_enterExitCompartment((char *)&parameters->seq2, kObjectCompartment3);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityAugust);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction69239528:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, August, function20, bool)
+ // Expose parameters as ISSI and ignore the default exposed parameters
+ EntityData::EntityParametersISSI *parameters = (EntityData::EntityParametersISSI*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ strcpy((char *)&parameters->seq1, "626");
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (getData()->clothes != kClothes2) {
+ strcpy((char *)&parameters->seq1, "666");
+ break;
+ }
+ // Fallback to next case
+
+ case kChapter4:
+ case kChapter5:
+ strcpy((char *)&parameters->seq1, "696");
+ break;
+ }
+
+ if (params->param1) {
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Gc").c_str());
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+ } else {
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Ec").c_str());
+ }
+
+ setCallback(1);
+ setup_enterExitCompartment((char *)&parameters->seq2, kObjectCompartment3);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1: {
+ getData()->location = kLocationOutsideCompartment;
+
+ Common::String sequence2 = Common::String::printf("%s%s", (char *)&parameters->seq2, "Pc");
+ strcpy((char *)&parameters->seq2, (char *)&parameters->seq1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, sequence2.c_str());
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ if (getProgress().chapter != kChapter3 || getState()->time >= kTime1998000) {
+ setCallback(3);
+ setup_playSound("AUG2095");
+ } else {
+ setCallback(2);
+ setup_playSound("AUG2094");
+ }
+ }
+ break;
+
+ case 2:
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction269436673);
+ strcpy((char *)&parameters->seq2, Common::String::printf("%s%s", (char *)&parameters->seq1, "Qc").c_str());
+
+ getEntities()->drawSequenceLeft(kEntityAugust, (char *)&parameters->seq2);
+ break;
+ }
+ break;
+
+ case kAction69239528:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(21, August, function21, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4 && params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM_GOTO(params->param8, getState()->timeTicks, 75, label_continue);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, (getProgress().eventMetAugust || getProgress().jacket != kJacketGreen) ? kCursorNormal : kCursorHand);
+ }
+
+ params->param8 = 0;
+
+label_continue:
+ if (getProgress().chapter != kChapter1)
+ break;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 6300)
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!params->param4
+ && !getProgress().eventMetAugust
+ && !params->param6
+ && (params->param1 - 4500) > getState()->time
+ && !getProgress().field_14) {
+ getProgress().field_14 = 2;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_8200;
+
+ setCallback(1);
+ setup_function20(false);
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().chapter == kChapter1 && !getProgress().eventMetAugust && getProgress().jacket == kJacketGreen) {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustHisCompartment);
+ break;
+ }
+ // Fallback to next case
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(12);
+ setup_playSound(rnd(2) ? getSound()->wrongDoorCath() : (rnd(2) ? "CAT1502" : "CAT1502A"));
+ } else {
+ setCallback(13);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 7 : 8);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function23((TimeValue)(params->param1 - 2700));
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(false, false);
+ break;
+
+ case 5:
+ if (getProgress().field_14 == 2)
+ getProgress().field_14 = 0;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param5 = 0;
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case 6:
+ getAction()->playAnimation(getObjects()->get(kObjectCompartment3).location2 == kObjectLocation1 ? kEventMeetAugustHisCompartmentBed : kEventMeetAugustHisCompartment);
+ getProgress().eventMetAugust = true;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getScenes()->loadSceneFromObject(kObjectCompartment3, true);
+ break;
+
+ case 7:
+ case 8:
+ ++params->param5;
+
+ switch(params->param5) {
+ default:
+ // Fall to next case
+ break;
+
+ case 1:
+ setCallback(9);
+ setup_playSound(rnd(2) ? "AUG1128A" : "AUG1128B");
+ return;
+
+ case 2:
+ setCallback(10);
+ setup_playSound(getProgress().eventMetAugust ? "AUG1128E" : "AUG1128G");
+ return;
+
+ case 3:
+ setCallback(11);
+ setup_playSound(getProgress().eventMetAugust ? "AUG1128F" : "AUG1128H");
+ return;
+ }
+ // Fallback to next case
+
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorTalk, (getProgress().eventMetAugust || getProgress().jacket != kJacketGreen) ? kCursorNormal : kCursorHand);
+
+ if (getCallback() == 12 || getCallback() == 13) {
+ params->param2 = 0;
+ params->param3 = 1;
+ } else {
+ params->param2= 1;
+ }
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_updateFromTime(75);
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_playSound("AUG1128I");
+ break;
+
+ case 16:
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction100906246);
+ break;
+
+ case 17:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityAugust, "626Lc");
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment3, true);
+ break;
+
+ case 18:
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment; // BUG: in the original, this is set to 6470
+ getEntities()->clearSequences(kEntityAugust);
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param4 = 0;
+ break;
+ }
+ break;
+
+ case kAction124697504:
+ getSound()->playSound(kEntityAugust, "CON1023A");
+
+ setCallback(18);
+ setup_enterExitCompartment("626Mc", kObjectCompartment3);
+ break;
+
+ case kAction192849856:
+ setCallback(17);
+ setup_enterExitCompartment("626Kc", kObjectCompartment3);
+ break;
+
+ case kAction221617184:
+ params->param4 = 1;
+ getSavePoints()->push(kEntityAugust, kEntityMertens, kAction102675536);
+
+ setCallback(14);
+ setup_playSound("CON1023");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, August, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+
+ getProgress().eventMetAugust = false;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(23, August, function23, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29 || getProgress().field_14 == 3) {
+ if (params->param3) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ } else {
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (!params->param2) {
+
+ if (!CURRENT_PARAM(1, 3))
+ CURRENT_PARAM(1, 3) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 3) >= getState()->timeTicks)
+ break;
+
+ if (!params->param5) {
+ setCallback(8);
+ setup_playSound("AUG1002B");
+ break;
+ }
+
+label_callback_8:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->timeTicks, 75)
+ getEntities()->exitCompartment(kEntityAugust, kObjectCompartment1, true);
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(9);
+ setup_enterExitCompartment("626Da", kObjectCompartment1);
+ } else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(10);
+ setup_enterExitCompartment3("626Da", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback_9:
+ if (params->param3 && params->param1 < getState()->time && !CURRENT_PARAM(1, 5)) {
+ CURRENT_PARAM(1, 5) = 1;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(12);
+ setup_enterExitCompartment("626Ea", kObjectCompartment1);
+ }
+ break;
+ }
+
+ if (!CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 1) >= getState()->timeTicks)
+ break;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ params->param6++;
+
+ switch (params->param6) {
+ default:
+ break;
+
+ case 1:
+ setCallback(5);
+ setup_playSound("LIB013");
+ return;
+
+ case 2:
+ setCallback(7);
+ setup_playSound("LIB012");
+ return;
+
+ case 3:
+ params->param8++;
+
+ if (params->param8 >= 3) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ params->param6 = 0;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ } else {
+
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? 8 : 7;
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param3) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(15);
+ setup_playSound("LIB012");
+ } else if (!params->param4) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(17);
+ setup_playSound("AUG1002A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ if (params->param3) {
+ getData()->location = kLocationInsideCompartment;
+
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustHisCompartmentBed : kEventMeetAugustHisCompartment;
+ } else {
+ params->param7 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMeetAugustTylerCompartmentBed : kEventMeetAugustTylerCompartment;
+ }
+
+ setCallback(14);
+ setup_savegame(kSavegameTypeEvent, kEventMeetAugustTylerCompartment);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+
+ params->param2 = 1;
+ } else {
+ setCallback(1);
+ setup_enterExitCompartment("626Aa", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityAugust, "626Ba");
+ getEntities()->enterCompartment(kEntityAugust, kObjectCompartment1, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound16("AUG1002B");
+ break;
+
+ case 6:
+ case 7:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, params->param4 ? kCursorNormal : kCursorTalk, kCursorHand);
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+
+ case 8:
+ params->param5 = 1;
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ getEntities()->clearSequences(kEntityAugust);
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ goto label_callback_9;
+
+ case 10:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventAugustFindCorpse);
+ break;
+
+ case 11:
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ getLogic()->gameOver(getEvent(kEventDinerAugustOriginalJacket) ? kSavegameTypeEvent2 : kSavegameTypeIndex,
+ getEvent(kEventDinerAugustOriginalJacket) ? kEventDinerAugustOriginalJacket : 1,
+ getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice,
+ true);
+ break;
+
+ case 12:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getAction()->playAnimation(kEventAugustFindCorpse);
+
+ if (getEvent(kEventDinerAugustOriginalJacket))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventDinerAugustOriginalJacket, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ else if (getProgress().eventCorpseMovedFromFloor)
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ else
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 14:
+ if (!params->param2)
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getAction()->playAnimation((EventIndex)params->param7);
+ getProgress().eventMetAugust = true;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_playSound("AUG1128A");
+ break;
+
+ case 16:
+ getObjects()->update(kObjectCompartment1, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 17:
+ params->param4 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityAugust, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, August, dinner)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugust);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+
+ getAction()->playAnimation(getEntities()->isInRestaurant(kEntityAlexei) ? kEventDinerAugustAlexeiBackground : kEventDinerAugust);
+ getProgress().eventMetAugust = true;
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, August, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getProgress().eventCorpseFound) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+
+ if (getState()->time > kTime1080000 && !params->param3) {
+ params->param3 = 1;
+
+ if (!params->param1) {
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction239072064);
+ params->param1 = 1;
+ }
+ }
+
+ if (getState()->time > kTime1093500 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_callSavepoint("010J", kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ }
+ break;
+
+ case kAction1:
+ params->param2 = 0;
+ getData()->inventoryItem = kItemNone;
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction191604416);
+
+ if (getProgress().jacket == kJacketGreen) {
+ setCallback(3);
+ setup_dinner();
+ } else {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventDinerAugustOriginalJacket);
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+
+ if (!getProgress().eventMetAugust)
+ params->param2 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+
+ case 3:
+ setup_function28();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityAugust, kEntityAlexei, kAction225182640);
+ getAction()->playAnimation(kEventDinerAugustOriginalJacket);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ getEntities()->drawSequenceRight(kEntityAugust, "010P");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction204704037);
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 6:
+ getProgress().field_14 = 2;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function23(kTimeNone);
+ break;
+
+ case 8:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, August, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventMetAugust || getProgress().field_14) {
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ } else {
+ getProgress().field_14 = 2;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function23((TimeValue)(getState()->time + 13500));
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(false, false);
+ break;
+
+ case 4:
+ if (getProgress().field_14 == 2)
+ getProgress().field_14 = 0;
+
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function19(false, false);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function21((TimeValue)(getState()->time + 900));
+ break;
+
+ case 7:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, August, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, August, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction304061224);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction203859488);
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B");
+ setup_function29();
+ break;
+ }
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)params->param1;
+ break;
+
+ case kAction170016384:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010G");
+
+ setCallback(2);
+ setup_playSound("AUG1053");
+ break;
+
+ case kAction268773672:
+ getData()->inventoryItem = kItemNone;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D");
+
+ setCallback(1);
+ setup_playSound("AUG1052");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, August, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().field_28 || (params->param2 && params->param3 == kTimeInvalid))
+ break;
+
+ if (getState()->time < kTime1134000) {
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer)
+ || getSound()->isBuffered("MRB1076") || getSound()->isBuffered("MRB1078") || getSound()->isBuffered("MRB1078A"))
+ params->param3 = (uint)getState()->time + 225;
+
+ if (params->param3 > getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+ getProgress().field_28 = 0;
+
+ setup_restaurant();
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ params->param1 = kItemNone;
+
+ setCallback(1);
+ setup_dinner();
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventMetAugust && getProgress().jacket == kJacketGreen)
+ params->param1 = kItemInvalid;
+
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param1);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H");
+ break;
+
+ case kAction168046720:
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction168627977:
+ getData()->inventoryItem = (InventoryItem)LOW_BYTE(params->param1);
+ break;
+
+ case kAction189426612:
+ params->param2 = 1;
+ break;
+
+ case kAction235257824:
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, August, restaurant)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kAction1:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 62);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 61);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 64);
+ break;
+
+ case kActionEndSound:
+ if (params->param1) {
+ getData()->inventoryItem = kItemNone;
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 61);
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 64);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAugustPresentAnna);
+ break;
+ }
+
+ if (params->param2) {
+ params->param2 = 0;
+ if (getProgress().eventMetAugust)
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(kEntityAugust, "Aug1003A");
+ } else {
+ getData()->inventoryItem = kItemNone;
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction201437056);
+
+ setCallback(8);
+ setup_draw("010P");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAugust, kEntityBoutarel, kAction135854206);
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction259136835);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_callSavepoint("010N", kEntityTables3, kActionDrawTablesWithChairs, "010K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction292758554);
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityAugust, "001K");
+ getSound()->playSound(kEntityAugust, "AUG1003");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getProgress().field_60 = 1;
+
+ params->param2 = 1;
+ break;
+
+ case 4:
+
+ break;
+
+ case 5:
+ case 7:
+ case 9:
+ getSavePoints()->push(kEntityAugust, kEntityBoutarel, kAction134466544);
+
+ setup_function31();
+ break;
+
+ case 6:
+ case 8:
+ getEntities()->drawSequenceRight(kEntityAugust, "803DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(getCallback() + 1);
+ setup_callbackActionOnDirection();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, August, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, false);
+ break;
+
+ case 2:
+ setCallback(2);
+ setup_function21(kTime1161000);
+ break;
+
+ case 3:
+ case 4:
+ if (getProgress().field_14 == 29) {
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 900));
+ } else {
+ setup_function32();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, August, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC_TIME(kTime1179000, (!getEntities()->isInSalon(kEntityAnna) || getEntities()->isInSalon(kEntityPlayer)), params->param6, 0);
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592);
+ UPDATE_PARAM_PROC_END
+
+ if (params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ if (!params->param4) {
+ params->param4 = (uint)getState()->time + 1800;
+ params->param5 = (uint)getState()->time + 9000;
+ }
+
+ if (params->param7 != kTimeInvalid && params->param4 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(params->param5, getEntities()->isInSalon(kEntityPlayer), params->param7, 0);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition("109D", kCarRestaurant, 56);
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (params->param3) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57)) {
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ params->param3 = true;
+ } else if (!getEntities()->isPlayerPosition(kCarRestaurant, 50)) {
+ params->param3 = false;
+ }
+ } else {
+ params->param3 = getEntities()->isPlayerPosition(kCarRestaurant, 56) && params->param1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("105A", kCarRestaurant, 57);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B");
+ params->param2 = 1;
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function19(false, false);
+ break;
+
+ case 7:
+ setup_function33();
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ params->param2 = 0;
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction159332865:
+ getEntities()->drawSequenceLeft(kEntityAugust, "106E");
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, August, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().eventMetAugust ? 1 : 2);
+ setup_function21(getProgress().eventMetAugust ? (TimeValue)(getState()->time + 9000) : kTimeBedTime);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, August, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityAugust) && getProgress().field_18 != 4)
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, August, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3970;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject11, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, August, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1755000, params->param2, kEntityAugust, kEntityServers0, kAction252568704);
+
+ if (getState()->time > kTime1773000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 62);
+
+ setCallback(2);
+ setup_callSavepoint("016C", kEntityTables0, kActionDrawTablesWithChairs, "016D");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustGoodMorning);
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityTables0, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustGoodMorning);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 61);
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 62);
+ getEntities()->drawSequenceRight(kEntityAugust, "803ES");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286534136);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function37();
+ break;
+
+ case 6:
+ if (!getEvent(kEventAugustGoodMorning))
+ getData()->inventoryItem = kItemInvalid;
+
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction219522616);
+ getEntities()->drawSequenceLeft(kEntityAugust, "016B");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityAugust, "016A");
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_playSound("AUG2113");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, August, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1791000, params->param2, 5, setup_function20, true);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ break;
+
+ case kActionDrawScene:
+ if (getState()->time > kTime1786500 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ if (params->param1) {
+ setCallback(2);
+ setup_draw("506C2");
+ } else {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_draw("506B2");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function20(true);
+ break;
+
+ case 3:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ case 6:
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, August, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1801800, params->param1, kEntityAugust, kEntityRebecca, kAction155980128);
+
+ TIME_CHECK_CALLBACK(kTime1820700, params->param2, 3, setup_callbackActionRestaurantOrSalon);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("109A", kCarRestaurant, 56);
+ break;
+
+ case 2:
+ getScenes()->loadSceneFromItemPosition(kItem3);
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case 3:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("109D2", kCarRestaurant, 56);
+ break;
+
+ case 4:
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ setCallback(5);
+ setup_function17(kTime1849500);
+ break;
+
+ case 5:
+ setup_function39();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound("AUG2114");
+ break;
+
+ case 7:
+ getEntities()->drawSequenceLeft(kEntityAugust, "108C");
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 56);
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 57);
+
+ setCallback(8);
+ setup_playSound("AUG2114A");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_playSound("AUG2115");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_draw2("108D1", "108D2", kEntityRebecca);
+ break;
+
+ case 10:
+ getEntities()->drawSequenceLeft(kEntityAugust, "109B");
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 56);
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 57);
+ getSavePoints()->push(kEntityAugust, kEntityRebecca, kAction125496184);
+ break;
+ }
+ break;
+
+ case kAction169358379:
+ getSavePoints()->push(kEntityAugust, kEntityRebecca, kAction155465152);
+ getEntities()->drawSequenceLeft(kEntityAugust, "108A");
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, August, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 1))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustArrivalInMunich);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustArrivalInMunich);
+ getSavePoints()->push(kEntityAugust, kEntityChapters, kActionChapter3);
+ getEntities()->clearSequences(kEntityAugust);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, August, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(41, August, function41, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getEvent(kEventAugustMerchandise)
+ && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarGreenSleeping) {
+ getAction()->playAnimation(kEventAugustMerchandise);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ }
+ }
+ break;
+
+ case kAction1:
+ params->param3 = kItemNone;
+ getData()->inventoryItem = kItemNone;
+
+ getAction()->playAnimation((getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) ? kEventAugustTalkGoldDay : kEventAugustTalkGold);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().eventMetAugust)
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1002" : "CAT1002A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEvent(kEventAugustMerchandise) && !getEvent(kEventAugustTalkGold) && !getEvent(kEventAugustTalkGoldDay))
+ params->param3 = kItemInvalid;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(42, August, function42, CarIndex, EntityPosition, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityAugust, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param4 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, getEvent(kEventAugustBringBriefcase) ? "AUG3103" : "AUG3100", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionExcuseMe:
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->excuseMe(kEntityAugust);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityAugust, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param3) {
+ params->param4 = 128;
+
+ if (!getEvent(kEventAugustBringBriefcase))
+ params->param4 = 147;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, August, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1953000, params->param2, kEntityAugust, kEntityAnna, kAction291662081);
+
+ // Set as same position as Anna
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityAnna)->entityPosition;
+ getData()->car = getEntityData(kEntityAnna)->car;
+ }
+
+ if (getState()->time > kTime2016000 && !params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function44();
+ }
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventAugustLunch);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803VS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A2");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B2");
+
+ if (!getEvent(kEventAugustLunch))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventAugustLunch);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 0;
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityAugust, "112G");
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, August, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122H", kCarRestaurant, 57);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventAugustMerchandise)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(2);
+ setup_function17(kTime2043000);
+ }
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ } else {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustMerchandise);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustMerchandise);
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityAugust, kPosition_6470, 500))
+ getData()->entityPosition = kPosition_5970;
+
+ getEntities()->updateEntity(kEntityAugust, kCarGreenSleeping, kPosition_6470);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car,
+ (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)),
+ getData()->direction == kDirectionUp);
+
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(true, false);
+ break;
+
+ case 5:
+ setup_function45();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, August, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2061000 && !params->param1) {
+ params->param1 = 1;
+ getData()->inventoryItem = kItemNone;
+
+ setup_function46();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityPlayer, "CAT1002");
+ getSound()->playSound(kEntityAugust, "AUG3102", SoundManager::kFlagInvalid, 15);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "506A2");
+ getData()->inventoryItem = kItem146; // TODO which item is that?
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, August, function46)
+ switch (savepoint.action) {
+ default:
+ TIME_CHECK_CALLBACK(kTime2088000, params->param1, 1, setup_function47);
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(2);
+ setup_draw("507B2");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setup_function48();
+ break;
+
+ case 2:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 43))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 34);
+
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, August, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function41(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ setCallback(3);
+ setup_updateFromTime(2700);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function41(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function19(false, false);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, August, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeCityLinz, params->param1, setup_function49);
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventAugustTalkCompartmentDoor) && !getEvent(kEventAugustTalkCompartmentDoorBlueRedingote)
+ && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCompartmentDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getData()->clothes = kClothes2;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoor);
+ getScenes()->processScene();
+
+ setCallback(2);
+ setup_function21(kTimeCityLinz);
+ break;
+
+ case 2:
+ setup_function49();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, August, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ setup_function50();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, August, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function51();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, August, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_5790, false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityAugust, kEntityTatiana, kAction191668032);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_540, true);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityAugust);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ getSavePoints()->push(kEntityAugust, kEntityAnna, kAction123712592);
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ setup_function52();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction169032608:
+ setCallback(3);
+ setup_function42(kCarRedSleeping, kPosition_3820, true);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, August, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ getData()->location = kLocationInsideCompartment;
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && !getEvent(kEventAugustBringEgg)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ if (!getEvent(kEventAugustTalkCompartmentDoorBlueRedingote) && !getEvent(kEventAugustBringEgg) && !getEvent(kEventAugustBringBriefcase)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventAugustBringEgg);
+ break;
+ }
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 6 : 7);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function42(kCarGreenSleeping, kPosition_6470, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function19(false, true);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityAugust, kEntityKahina, kAction134611040);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustBringBriefcase);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function17);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 13);
+
+ setup_function53();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventAugustBringEgg);
+ getScenes()->processScene();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventAugustTalkCompartmentDoorBlueRedingote);
+ getScenes()->processScene();
+ break;
+
+ case 6:
+ case 7:
+ setCallback(8);
+ setup_playSound("AUG1128F");
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, August, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(2700);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function20(false);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setup_function54();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, August, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4 || params->param2 || getProgress().field_44)
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2 && params->param1) {
+ UPDATE_PARAM(params->param5, getState()->time, params->param1);
+
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventAugustTalkCigar);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isPlayerPosition(kCarRestaurant, 60) || params->param3) {
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ } else if (!params->param2) {
+ getEntities()->updatePositionEnter(kEntityAugust, kCarRestaurant, 57);
+ getEntities()->drawSequenceRight(kEntityAugust, "105C3");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105A3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityAugust, kEntityAbbot, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityAugust, "105B3");
+ params->param4 = 1;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventAugustTalkCigar);
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param3 ? "122B" : "105B3");
+ getScenes()->processScene();
+
+ params->param1 = 9000;
+ params->param4 = 0;
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ params->param2 = 0;
+
+ if (getEvent(kEventAugustTalkCigar))
+ params->param1 = 9000;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ params->param2 = 1;
+ params->param3 = 1;
+ break;
+
+ case kAction136196244:
+ params->param2 = 1;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, August, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition("105D3", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(true, false);
+ break;
+
+ case 4:
+ setup_function56();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, August, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isPlayerPosition(kCarGreenSleeping, 43)) {
+ setCallback(1);
+ setup_draw("507B3");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityAugust, "507A3");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, August, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, August, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("803WS");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityAugust, "010A3");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(5);
+ setup_callSavepointNoDrawing(kEntityTables3, kAction136455232, "BOGUS");
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function59();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, August, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ getSavePoints()->push(kEntityAugust, kEntityPascale, kAction190605184);
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction123793792:
+ setup_function60();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, August, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ bool pushSavepoint = false;
+ if (!params->param2) {
+ pushSavepoint = true;
+ params->param2 = (uint)getState()->time + 450;
+ }
+
+ if (params->param2 < getState()->time) {
+ pushSavepoint = true;
+ params->param2 = kTimeInvalid;
+ }
+
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction207330561);
+
+ if (!params->param1)
+ break;
+
+ UPDATE_PARAM(params->param3, getState()->time, 9000);
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("010J3", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityAugust, kEntityServers0, kAction286403504);
+ setup_function61();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010B3");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction201964801:
+ getEntities()->drawSequenceLeft(kEntityAugust, "010H3");
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, August, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceRight(kEntityAugust, "803FS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityAugust);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function19(false, false);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function21((TimeValue)(getState()->time + 4500));
+ break;
+
+ case 4:
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, August, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 900);
+
+ getSound()->playSound(kEntityAugust, "Aug4003A");
+
+ setCallback(5);
+ setup_updatePosition("122C", kCarRestaurant, 57);
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("696Ec", kObjectCompartment3);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_updatePosition("122A", kCarRestaurant, 57);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ getSavePoints()->push(kEntityAugust, kEntityServers1, kAction291721418);
+ break;
+ }
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+ break;
+
+ case kAction125826561:
+ setup_function63();
+ break;
+
+ case kAction134486752:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(63, August, function63)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 1800)
+ getData()->inventoryItem = kItemInvalid;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2488500 && !params->param4) {
+ params->param4 = 1;
+ getData()->inventoryItem = kItemNone;
+ setup_function64();
+ break;
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, params->param1);
+
+ params->param2 = (params->param6 < 1 ? 1 : 0);
+
+ getEntities()->drawSequenceLeft(kEntityAugust, params->param2 ? "122H" : "122F");
+
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ params->param5 = 0;
+ break;
+
+ case kAction1:
+ if (getEntities()->isInSalon(kEntityAlexei))
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function44);
+
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustDrink);
+ break;
+
+ case kActionDefault:
+ params->param1 = 5 * (3 * rnd(20) + 15);
+ getEntities()->drawSequenceLeft(kEntityAugust, "122F");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventAugustDrink);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+
+ setup_function64();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(64, August, function64)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ params->param1 = (uint)getState()->time + 1800;
+
+ if (params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time > kTime2430000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("122J", kCarRestaurant, 57);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityAugust, "122H");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 57))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 50);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment2("696Dc", kObjectCompartment3);
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityAugust);
+ setup_function65();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(65, August, function65)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityAugust);
+
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityAugust))
+ getSound()->playSound(kEntityAugust, "AUG1057"); // August snoring
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(66, August, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityAugust);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(67, August, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function68();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(68, August, function68)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ ++params->param3;
+
+ switch (params->param3) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound("Aug5002");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_playSound("Aug5002A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("Aug5002B");
+ break;
+ }
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 5:
+ getObjects()->update(kObjectCompartment3, kEntityAugust, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 6:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction203078272:
+ getSavePoints()->push(kEntityAugust, kEntityTatiana, kAction203078272);
+
+ setup_unhookCars();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(69, August, unhookCars)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getSavePoints()->pushAll(kEntityAugust, kAction135800432);
+ setup_nullfunction();
+ break;
+
+ case kActionDefault:
+ getSound()->processEntries();
+ if (getSound()->isBuffered("ARRIVE"))
+ getSound()->removeFromQueue("ARRIVE");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventAugustUnhookCarsBetrayal);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(getProgress().field_C ? kEventAugustUnhookCarsBetrayal : kEventAugustUnhookCars);
+ getEntities()->clearSequences(kEntityAugust);
+ getSound()->resetState();
+ getSound()->playSound(kEntityPlayer, "MUS050");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1);
+ getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5);
+
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42)
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(70, August)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/august.h b/engines/lastexpress/entities/august.h
new file mode 100644
index 0000000000..1a883a0ed5
--- /dev/null
+++ b/engines/lastexpress/entities/august.h
@@ -0,0 +1,275 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_AUGUST_H
+#define LASTEXPRESS_AUGUST_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class August : public Entity {
+public:
+ August(LastExpressEngine *engine);
+ ~August() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Call a savepoint
+ *
+ * @param param1 The entity
+ * @param param2 The action
+ * @param seq The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_3(callSavepointNoDrawing, EntityIndex entity, ActionIndex action, const char *sequence)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function17, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param param1 The car
+ * @param param2 The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function19, bool, bool)
+
+ DECLARE_FUNCTION_1(function20, bool)
+ DECLARE_FUNCTION_1(function21, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(function23, TimeValue timeValue)
+ DECLARE_FUNCTION(dinner)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(restaurant)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_2(function41, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_3(function42, CarIndex car, EntityPosition entityPosition, bool)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+ DECLARE_FUNCTION(function63)
+ DECLARE_FUNCTION(function64)
+ DECLARE_FUNCTION(function65)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function68)
+ DECLARE_FUNCTION(unhookCars)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_AUGUST_H
diff --git a/engines/lastexpress/entities/boutarel.cpp b/engines/lastexpress/entities/boutarel.cpp
new file mode 100644
index 0000000000..b4378c95c4
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.cpp
@@ -0,0 +1,1260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/boutarel.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Boutarel::Boutarel(LastExpressEngine *engine) : Entity(engine, kEntityBoutarel) {
+ ADD_CALLBACK_FUNCTION(Boutarel, reset);
+ ADD_CALLBACK_FUNCTION(Boutarel, playSound);
+ ADD_CALLBACK_FUNCTION(Boutarel, draw);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Boutarel, updatePosition);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Boutarel, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Boutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(Boutarel, function11);
+ ADD_CALLBACK_FUNCTION(Boutarel, enterTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, leaveTableWithMmeBoutarel);
+ ADD_CALLBACK_FUNCTION(Boutarel, function14);
+ ADD_CALLBACK_FUNCTION(Boutarel, function15);
+ ADD_CALLBACK_FUNCTION(Boutarel, function16);
+ ADD_CALLBACK_FUNCTION(Boutarel, function17);
+ ADD_CALLBACK_FUNCTION(Boutarel, function18);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(Boutarel, function20);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function22);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function25);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function28);
+ ADD_CALLBACK_FUNCTION(Boutarel, function29);
+ ADD_CALLBACK_FUNCTION(Boutarel, function30);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function33);
+ ADD_CALLBACK_FUNCTION(Boutarel, function34);
+ ADD_CALLBACK_FUNCTION(Boutarel, function35);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(Boutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Boutarel, function38);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Boutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Boutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Boutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Boutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(5, Boutarel, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Boutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Boutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_6470, kPosition_6130, kCarRedSleeping, kObjectCompartmentC, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Boutarel, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Boutarel, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Boutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+
+ if (getInventory()->hasItem(kItemPassengerList) && getState()->time > kTime1089000)
+ getSound()->playSound(kEntityPlayer, "CAT1022");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Boutarel, function11, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && ENTITY_PARAM(0, 2)) {
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 1) = 0;
+
+ setCallback(5);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ break;
+
+ case kActionDefault:
+ if (params->param1) {
+ if (getProgress().chapter == kChapter4) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("607Hc", kObjectCompartmentC);
+ } else {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_enterExitCompartment("607Dc", kObjectCompartmentC);
+ }
+ } else {
+ setCallback(3);
+ setup_enterExitCompartment("607Bc", kObjectCompartmentC);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 2:
+ case 3:
+ if (getCallback() == 2)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ else
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ // Fallback to next case
+
+ case 1:
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction101107728);
+
+ setCallback(4);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityBoutarel);
+ break;
+
+ case 5:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_draw("812US");
+ break;
+
+ case 6:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityBoutarel, "MRB1075", SoundManager::kFlagInvalid, 60);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityBoutarel, "MRB3101");
+ break;
+ }
+
+ setCallback(7);
+ setup_enterTableWithMmeBoutarel();
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Boutarel, enterTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008A3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008A2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008A1");
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->updateFrame(kEntityBoutarel);
+ getEntityData(kEntityMmeBoutarel)->location = getData()->location;
+ getEntityData(kEntityTables2)->location = getData()->location;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Boutarel, leaveTableWithMmeBoutarel)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityBoutarel, kEntityTables2, kActionDrawTablesWithChairs, "008F");
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables2, "008E3");
+ getEntities()->drawSequenceRight(kEntityMmeBoutarel, "008E2");
+ getEntities()->drawSequenceRight(kEntityBoutarel, "008E1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Boutarel, function14, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityBoutarel, "MRB1079");
+
+ setCallback(2);
+ setup_leaveTableWithMmeBoutarel();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction326144276);
+ getEntities()->drawSequenceRight(kEntityBoutarel, "812DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityBoutarel);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntityData(kEntityFrancois)->entityPosition = kPosition_540;
+ getEntityData(kEntityMmeBoutarel)->entityPosition = kPosition_540;
+ getData()->entityPosition = kPosition_540;
+
+ getEntityData(kEntityFrancois)->location = kLocationOutsideCompartment;
+ getEntityData(kEntityMmeBoutarel)->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ getSavePoints()->push(kEntityBoutarel, kEntityMmeBoutarel, kAction100901266);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityFrancois, kAction100901266);
+
+ setCallback(5);
+ setup_updateFromTime(450);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 6:
+ setCallback(params->param1 ? 7: 8);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 7:
+ case 8:
+ getEntities()->clearSequences(kEntityBoutarel);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Boutarel, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(params->param1 ? 1 : 2);
+ setup_enterExitCompartment(params->param1 ? "607Dc" : "607Bc", kObjectCompartmentC);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updatePosition(params->seq, kCarRestaurant, 52);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters:
+// bool
+// const char *
+IMPLEMENT_FUNCTION_IS(16, Boutarel, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updatePosition((const char *)&params->seq, kCarRestaurant, 52);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ setCallback(params->param1 ? 4 : 5);
+ setup_enterExitCompartment2(params->param1 ? "607Gc" : "607Ac", kObjectCompartmentC);
+ break;
+
+ case 4:
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(17, Boutarel, function17, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param6);
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 90)
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ } else {
+ params->param7 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, (char *)&params->seq);
+ break;
+
+ case kActionDrawScene:
+ params->param5 = (getEntities()->isPlayerPosition(kCarRestaurant, 52) ? 1 : 0);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(18, Boutarel, function18, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (savepoint.param.intValue == 50) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ } else if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1511" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "MRB1001" : "MRB1001A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject50, kEntityBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction221683008:
+ setCallback(7);
+ setup_playSound("MRB1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Boutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityBoutarel, kAction203520448, 0);
+ getSavePoints()->addData(kEntityBoutarel, kAction237889408, 1);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject42, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Boutarel, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (!params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ setCallback(3);
+ setup_playSound("MRB1078A");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(false);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_playSound("MRB1076");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ TIME_CHECK_CALLBACK_1(kTime1138500, params->param4, 4, setup_function14, false);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityBoutarel, kEntityCooks, kAction224849280);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction134466544:
+ params->param2 = 0;
+ break;
+
+ case kAction135854206:
+ params->param2 = 1;
+ break;
+
+ case kAction168717392:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+
+ if (!params->param2) {
+ setCallback(5);
+ setup_playSound("MRB1078");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Boutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(kTime1071000, "101A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16(false, "101B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18(kTime1102500);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 54) || getEntities()->isPlayerPosition(kCarRedSleeping, 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 10);
+
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(4);
+ setup_playSound("MRB1074");
+ break;
+
+ case 4:
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 54);
+ getEntities()->updatePositionExit(kEntityBoutarel, kCarRedSleeping, 44);
+
+ setCallback(5);
+ setup_function20();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18(kTimeEnterChalons);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(false, "102A");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function17(kTime1183500, "102B");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18(kTime1215000);
+ break;
+
+ case 10:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Boutarel, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Boutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Boutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1759500, params->param2, 1, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInRestaurant(kEntityPlayer) && !params->param1) {
+ getSound()->playSound(kEntityBoutarel, "MRB2001");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Boutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Boutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Boutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ setup_function28();
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Boutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Boutarel, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 450)
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ UPDATE_PARAM_PROC_END
+
+ if (!params->param1)
+ break;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)
+ && getEntities()->isInRestaurant(kEntityAugust)
+ && !getSound()->isBuffered(kEntityBoutarel)
+ && params->param3 != kTimeInvalid) {
+
+ if (getState()->time <= kTime1998000)
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)(getState()->time + 450);
+
+ if (params->param3 < getState()->time || getState()->time > kTime1998000) {
+ params->param3 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MRB3102");
+ break;
+ }
+ }
+
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK_1(kTime2002500, params->param4, 1, setup_function14, true);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ params->param1 = 1;
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Boutarel, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Boutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Boutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2367000, params->param1, setup_function33);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "510");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Boutarel, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ TIME_CHECK_CALLBACK_1(kTime2389500, params->param2, 3, setup_function14, false);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function11(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008B");
+
+ setCallback(2);
+ setup_updateFromTime(450);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityBoutarel, kEntityServers1, kAction256200848);
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+
+ case kAction122288808:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "008D");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Boutarel, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2470500, params->param1, setup_function35);
+
+ if (getState()->time > kTime2457000 && getEvent(kEventAugustDrink)) {
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction159003408);
+
+ setCallback(1);
+ setup_function15(false, "102A");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityBoutarel, kEntityAbbot, kAction101687594);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(false, "102C");
+ break;
+
+ case 3:
+ case 7:
+ setup_function35();
+ break;
+
+ case 4:
+ case 8:
+ if (getState()->time >= kTime2470500) {
+ setup_function35();
+ break;
+ }
+
+ if (getEvent(kEventAugustDrink)) {
+ setCallback(5);
+ setup_function15(false, "102A");
+ } else {
+ setCallback(8);
+ setup_function18((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17(kTime2479500, "102B");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function16(false, "102C");
+ break;
+
+ case 9:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction123199584);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getSavePoints()->push(kEntityBoutarel, kEntityCoudert, kAction88652208);
+ break;
+
+ case kAction125039808:
+ setCallback(4);
+ setup_function18(kTime2457000);
+ break;
+
+ case kAction221683008:
+ setCallback(9);
+ setup_playSound("Mrb1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Boutarel, function35)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Boutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Boutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function38();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Boutarel, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(39, Boutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/boutarel.h b/engines/lastexpress/entities/boutarel.h
new file mode 100644
index 0000000000..0ac051df77
--- /dev/null
+++ b/engines/lastexpress/entities/boutarel.h
@@ -0,0 +1,188 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_BOUTAREL_H
+#define LASTEXPRESS_BOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Boutarel : public Entity {
+public:
+ Boutarel(LastExpressEngine *engine);
+ ~Boutarel() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, bool)
+ DECLARE_FUNCTION(enterTableWithMmeBoutarel)
+ DECLARE_FUNCTION(leaveTableWithMmeBoutarel)
+ DECLARE_FUNCTION_1(function14, bool)
+ DECLARE_FUNCTION_2(function15, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function16, bool, const char *sequence)
+ DECLARE_FUNCTION_2(function17, TimeValue timeValue, const char *sequence)
+ DECLARE_FUNCTION_1(function18, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function38)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BOUTAREL_H
diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp
new file mode 100644
index 0000000000..a057da23d3
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.cpp
@@ -0,0 +1,1820 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/chapters.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/alouan.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/boutarel.h"
+#include "lastexpress/entities/coudert.h"
+#include "lastexpress/entities/cooks.h"
+#include "lastexpress/entities/francois.h"
+#include "lastexpress/entities/gendarmes.h"
+#include "lastexpress/entities/hadija.h"
+#include "lastexpress/entities/ivo.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/kronos.h"
+#include "lastexpress/entities/mahmud.h"
+#include "lastexpress/entities/max.h"
+#include "lastexpress/entities/mertens.h"
+#include "lastexpress/entities/milos.h"
+#include "lastexpress/entities/mmeboutarel.h"
+#include "lastexpress/entities/pascale.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/servers0.h"
+#include "lastexpress/entities/servers1.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tatiana.h"
+#include "lastexpress/entities/vassili.h"
+#include "lastexpress/entities/verges.h"
+#include "lastexpress/entities/vesna.h"
+#include "lastexpress/entities/yasmin.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Chapters::Chapters(LastExpressEngine *engine) : Entity(engine, kEntityChapters) {
+ ADD_CALLBACK_FUNCTION(Chapters, savegame);
+ ADD_CALLBACK_FUNCTION(Chapters, enterStation);
+ ADD_CALLBACK_FUNCTION(Chapters, exitStation);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1);
+ ADD_CALLBACK_FUNCTION(Chapters, resetMainEntities);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1End);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter1Next);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, viennaEvents);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter5);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Init);
+ ADD_CALLBACK_FUNCTION(Chapters, chapter4Handler);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Chapters, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Chapters, enterStation, CityIndex)
+ enterExitStation(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Chapters, exitStation)
+ enterExitStation(savepoint, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Chapters, chapter1)
+ if (savepoint.action == kActionDefault) {
+ getSavePoints()->addData(kEntityChapters, kAction171843264, 0);
+ setup_chapter1Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Chapters, resetMainEntities)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_reset);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_reset);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_reset);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_reset);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_reset);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_reset);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_reset);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_reset);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_reset);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_reset);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_reset);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_reset);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_reset);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_reset);
+ RESET_ENTITY_STATE(kEntityGendarmes, Gendarmes, setup_reset);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_reset);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_reset);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_reset);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_reset);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_reset);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_reset);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_reset);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_reset);
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Chapters, chapter1End)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityChapters, "MUS0009", SoundManager::kFlagDefault);
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ getEntities()->clearSequences(kEntityChapters);
+ getSound()->processEntry(kEntityChapters);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->resetState();
+
+ ENTITY_PARAM(0, 4) = 7;
+
+ getSound()->playSteam(kCityPolice);
+
+ getAction()->playAnimation(kEventCathDream);
+
+ getState()->timeDelta = 3;
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 0;
+
+ getObjects()->update(kObject63, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ } else {
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getSound()->playSound(kEntityPlayer, "LIB015", SoundManager::kFlagDefault, 15);
+
+ if (!getSound()->isBuffered(kEntityChapters))
+ getSound()->playSound(kEntityChapters, "MUS009", SoundManager::kFlagDefault);
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 38);
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_function19);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_function22);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_function16);
+ RESET_ENTITY_STATE(kEntityCooks, Cooks, setup_function7);
+
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function42);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_chapter1Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter1Handler);
+
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ RESET_ENTITY_STATE(kEntityKronos, Kronos, setup_function10);
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function13);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function34);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function34);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function24);
+ RESET_ENTITY_STATE(kEntityVassili, Vassili, setup_function7);
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function26);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function18);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function15);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function17);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function11);
+ RESET_ENTITY_STATE(kEntityFrancois, Francois, setup_function20);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function16);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function22);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function27);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function5);
+ RESET_ENTITY_STATE(kEntityMahmud, Mahmud, setup_resetChapter);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function10);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function12);
+ RESET_ENTITY_STATE(kEntityHadija, Alouan, setup_function12);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->processEntries();
+
+ if (getSound()->isBuffered("CON1505"))
+ getSound()->processEntry("CON1505");
+
+ if (getSound()->isBuffered("AUG1057"))
+ getSound()->processEntry("AUG1057");
+
+ if (getSound()->isBuffered("ZFX1005"))
+ getSound()->processEntry("ZFX1005");
+
+ if (getSound()->isBuffered("ZFX1006"))
+ getSound()->processEntry("ZFX1006");
+
+ if (getSound()->isBuffered("ZFX1007"))
+ getSound()->processEntry("ZFX1007");
+
+ if (getSound()->isBuffered("ZFX1007A"))
+ getSound()->processEntry("ZFX1007A");
+
+ if (getSound()->isBuffered("ZFX1007B"))
+ getSound()->processEntry("ZFX1007B");
+
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ // FIXME add event pump ?
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ getProgress().field_84 = true;
+
+ getScenes()->loadSceneFromPosition(kCarLocomotive, 75);
+ getInventory()->show();
+
+ getState()->time = kTime1492200;
+ getProgress().field_18 = 4;
+ getState()->timeDelta = 0;
+
+ getObjects()->update(kObject63, kEntityChapters, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ getProgress().isTrainRunning = false;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction225358684:
+ params->param2++;
+
+ if (params->param2 >= 3) {
+
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSound(kEntityPlayer, "LIB031");
+
+ if (params->param2 == 3) {
+ getData()->car = kCarGreenSleeping;
+ getEntities()->drawSequenceLeft(kEntityChapters, "JUGL");
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Chapters, chapter1Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().chapter = kChapter1;
+ getSound()->resetState();
+
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 0;
+ getProgress().isTrainRunning = true;
+ getProgress().portrait = kPortraitOriginal;
+ getProgress().field_18 = 1;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemTelegram);
+ getInventory()->addItem(kItemArticle);
+
+ getInventory()->setLocationAndProcess(kItemScarf, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemParchemin, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemGreenJacket, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemCorpse, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPassengerList, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem22, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItemPaper, kObjectLocation1);
+
+ getProgress().field_7C = 1;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ for (uint i = kObjectCompartment1; i <= kObjectCompartment8; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = kObjectCompartmentA; i <= kObjectCompartmentH; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectTrainTimeTable, kObjectLocation1);
+ getObjects()->updateLocation2(kObject98, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation1);
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRedSleepingCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject28, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject56, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject54, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject59, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject66, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject69, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObject98, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHandKnock);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_chapter1Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+#define PLAY_STEAM() { \
+ getSound()->resetState(); \
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); \
+ ENTITY_PARAM(0, 2) = 0; \
+ }
+
+IMPLEMENT_FUNCTION(8, Chapters, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning || getState()->time >= kTime1458000)
+ goto label_processStations;
+
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param2)
+ // Play sound FX
+ getSound()->playLocomotiveSound();
+
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+
+label_processStations:
+ // Process stations
+ TIME_CHECK_CALLBACK_2(kTime1039500, params->param7, 1, setup_savegame, kSavegameTypeTime, kTimeNone);
+
+label_enter_epernay:
+ // Entering Epernay station
+ TIME_CHECK_CALLBACK_2(kTimeEnterEpernay, params->param8, 1, setup_enterStation, "Epernay", kCityEpernay);
+
+label_exit_epernay:
+ // Exiting Epernay station
+ if (getState()->time > kTimeExitEpernay && !CURRENT_PARAM(1, 1)) {
+ CURRENT_PARAM(1, 1) = 1;
+ params->param4 = 1;
+ setCallback(3);
+ setup_exitStation("Epernay");
+ break;
+ }
+
+label_epernay_police:
+ if (params->param5 && !ENTITY_PARAM(0, 2)) {
+ setCallback(4);
+ setup_exitStation("Unschedu");
+ break;
+ }
+
+label_enter_chalons:
+ if (getState()->time > kTimeEnterChalons && !CURRENT_PARAM(1, 2)) {
+ CURRENT_PARAM(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+
+ // Skip to callback 18 checks
+ if (params->param1)
+ goto label_exit_strasbourg;
+
+ // Entering Chalons station
+ TIME_CHECK_CALLBACK_2(kTimeEnterChalons, CURRENT_PARAM(1, 3), 5, setup_enterStation, "Chalons", kCityChalons);
+
+label_exit_chalons:
+ // Exiting Chalons station
+ TIME_CHECK_CALLBACK_1(kTimeExitChalons, CURRENT_PARAM(1, 4), 6, setup_exitStation, "Chalons");
+
+label_enter_barleduc:
+ // Entering Bar-Le-Duc station
+ TIME_CHECK_CALLBACK_2(kTimeCityBarLeDuc, CURRENT_PARAM(1, 5), 7, setup_enterStation, "BarLeDuc", kCityBarleduc);
+
+label_exit_barleduc:
+ // Exiting Bar-Le-Duc station
+ TIME_CHECK_CALLBACK_1(kTimeExitBarLeDuc, CURRENT_PARAM(1, 6), 8, setup_exitStation, "BarLeDuc");
+
+label_enter_nancy:
+ if (getState()->time > kTime1260000 && !CURRENT_PARAM(1, 7)) {
+ CURRENT_PARAM(1, 7) = 1;
+ getState()->timeDelta = 1;
+ }
+
+ // Entering Nancy station
+ TIME_CHECK_CALLBACK_2(kTimeCityNancy, CURRENT_PARAM(1, 8), 9, setup_enterStation, "Nancy", kCityNancy);
+
+label_exit_nancy:
+ // Exiting Nancy station
+ TIME_CHECK_CALLBACK_1(kTimeExitNancy, CURRENT_PARAM(2, 1), 10, setup_exitStation, "Nancy");
+
+label_enter_luneville:
+ // Entering Luneville station
+ TIME_CHECK_CALLBACK_2(kTimeCityLuneville, CURRENT_PARAM(2, 2), 11, setup_enterStation, "Luneville", kCityLuneville);
+
+label_exit_luneville:
+ // Exiting Luneville station
+ TIME_CHECK_CALLBACK_1(kTimeExitLuneville, CURRENT_PARAM(2, 3), 12, setup_exitStation, "Luneville");
+
+label_enter_avricourt:
+ // Entering Avricourt station
+ TIME_CHECK_CALLBACK_2(kTimeCityAvricourt, CURRENT_PARAM(2, 4), 13, setup_enterStation, "Avricourt", kCityAvricourt);
+
+label_exit_avricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_CALLBACK_1(kTimeExitAvricourt, CURRENT_PARAM(2, 5), 14, setup_exitStation, "Avricourt");
+
+label_enter_deutschavricourt:
+ // Entering Deutsch-Avricourt station
+ TIME_CHECK_CALLBACK_2(kTimeCityDeutschAvricourt, CURRENT_PARAM(2, 6), 15, setup_enterStation, "DeutschA", kCityDeutschAvricourt);
+
+label_exit_deutschavricourt:
+ // Exiting Avricourt station
+ TIME_CHECK_CALLBACK_1(kTimeExitDeutschAvricourt, CURRENT_PARAM(2, 7), 16, setup_exitStation, "DeutschA");
+
+label_enter_strasbourg:
+ TIME_CHECK_CALLBACK_2(kTimeCityStrasbourg, CURRENT_PARAM(2, 8), 17, setup_savegame, kSavegameTypeTime, kTimeNone);
+
+label_exit_strasbourg:
+ // Exiting Strasbourg station
+ TIME_CHECK_CALLBACK_1(kTimeExitStrasbourg, CURRENT_PARAM(3, 1), 19, setup_exitStation, "Strasbou");
+
+label_enter_badenoos:
+ // Entering Baden Oos station
+ TIME_CHECK_CALLBACK_2(kTimeCityBadenOos, CURRENT_PARAM(3, 2), 20, setup_enterStation, "BadenOos", kCityBadenOos);
+
+label_exit_badenoos:
+ // Exiting Baden Oos station
+ TIME_CHECK_CALLBACK_1(kTimeExitBadenOos, CURRENT_PARAM(3, 3), 21, setup_exitStation, "BadenOos");
+
+label_chapter1_next:
+ if (getState()->time > kTimeChapter1End3 && ! CURRENT_PARAM(3, 4)) {
+ CURRENT_PARAM(3, 4) = 1;
+ setup_chapter1Next();
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+
+ if (params->param4) {
+ getSavePoints()->push(kEntityChapters, getProgress().field_24 ? kEntityVerges : kEntityMertens, getProgress().field_24 ? kAction168187490 : kAction224122407);
+ params->param4 = 0;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ params->param2 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ getState()->time = kTimeChapter1;
+ getState()->timeDelta = 3;
+ params->param3 = 1;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_enter_epernay;
+
+ case 2:
+ goto label_exit_epernay;
+
+ case 3:
+ goto label_epernay_police;
+
+ case 4:
+ params->param5 = 0;
+ goto label_enter_chalons;
+
+ case 5:
+ goto label_exit_chalons;
+
+ case 6:
+ goto label_enter_barleduc;
+
+ case 7:
+ goto label_exit_barleduc;
+
+ case 8:
+ goto label_enter_nancy;
+
+ case 9:
+ goto label_exit_nancy;
+
+ case 10:
+ goto label_enter_luneville;
+
+ case 11:
+ goto label_exit_luneville;
+
+ case 12:
+ goto label_enter_avricourt;
+
+ case 13:
+ goto label_exit_avricourt;
+
+ case 14:
+ goto label_enter_deutschavricourt;
+
+ case 15:
+ goto label_exit_deutschavricourt;
+
+ case 16:
+ getState()->time = kTimeEnterStrasbourg;
+ goto label_enter_strasbourg;
+
+ case 17:
+ getProgress().field_18 = 1;
+ setCallback(18);
+ setup_enterStation("Strasbou", kCityStrasbourg);
+ break;
+
+ case 18:
+ goto label_exit_strasbourg;
+
+ case 19:
+ getState()->timeDelta = 1;
+ goto label_enter_badenoos;
+
+ case 20:
+ goto label_exit_badenoos;
+
+ case 21:
+ goto label_chapter1_next;
+
+ case 22:
+ params->param5 = 1;
+ break;
+
+ case 23:
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction169629818:
+ setCallback(22);
+ setup_enterStation("Unschedu", kCityPolice);
+ break;
+
+ case kActionEndChapter:
+ getProgress().field_18 = 3;
+
+ if (getState()->time >= kTimeChapter1End) {
+ setup_chapter1Next();
+ } else {
+ setCallback(23);
+ setup_chapter1End();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Chapters, chapter1Next)
+ if (savepoint.action == kActionDefault) {
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ setup_chapter2();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Chapters, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 2 in case it hasn't been done before
+ if (getProgress().chapter != kChapter2) {
+ getProgress().chapter = kChapter2;
+ getEntities()->setupChapter(kChapter2);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter2;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd2)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd2);
+ setup_chapter2Init();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Chapters, chapter2Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ getProgress().field_18 = 1;
+ getProgress().isTrainRunning = true;
+ getProgress().eventCorpseFound = true;
+
+ // Switch to green jacket/portrait
+ getProgress().jacket = kJacketGreen;
+ getProgress().portrait = kPortraitGreen;
+
+ // Setup inventory & items location
+ getInventory()->addItem(kItemGreenJacket);
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getInventory()->setLocationAndProcess(kItemBeetle, kObjectLocation3);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+
+ for (uint i = 1; i < 9; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ for (uint i = 33; i < 40; i++) {
+ getObjects()->updateLocation2((ObjectIndex)i, kObjectLocation2);
+ }
+
+ params->param1 = 40;
+
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ // Reset sound cache
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getAction()->playAnimation(kEventTrainPassing);
+
+ if (getInventory()->hasItem(kItemScarf))
+ getScenes()->loadScene(kScene41);
+ else
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 79);
+
+ setup_chapter2Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Chapters, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getProgress().isTrainRunning)
+ break;
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ break;
+
+ case kActionChapter3:
+ setup_chapter3();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Chapters, chapter3)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 3 in case it hasn't been done before
+ if (getProgress().chapter != kChapter3) {
+ getProgress().chapter = kChapter3;
+ getEntities()->setupChapter(kChapter3);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter3;
+ getState()->timeDelta = 5;
+
+ setup_chapter3Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Chapters, chapter3Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation1);
+ getInventory()->setLocationAndProcess(kItem3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment1, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter3Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Chapters, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, params->param1)
+ getSound()->playLocomotiveSound();
+
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, params->param2)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterSalzbourg, params->param6, 1, setup_enterStation, "Salzburg", kCitySalzbourg);
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTimeExitSalzbourg, params->param7, 2, setup_exitStation, "Salzburg");
+
+label_callback_2:
+ TIME_CHECK_CALLBACK_2(kTimeEnterAttnangPuchheim, params->param8, 3, setup_enterStation, "Attnang", kCityAttnangPuchheim);
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTimeExitAttnangPuchheim, CURRENT_PARAM(1, 1), 4, setup_exitStation, "Attnang");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_2(kTimeEnterWels, CURRENT_PARAM(1, 2), 5, setup_enterStation, "Wels", kCityWels);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK_1(kTimeEnterWels, CURRENT_PARAM(1, 3), 6, setup_exitStation, "Wels");
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_2(kTimeEnterLinz, CURRENT_PARAM(1, 4), 7, setup_enterStation, "Linz", kCityLinz);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTimeCityLinz, CURRENT_PARAM(1, 5), 8, setup_exitStation, "Linz");
+
+label_callback_8:
+ if (getState()->time > kTime2187000 && !CURRENT_PARAM(1, 6)) {
+ CURRENT_PARAM(1, 6) = 1;
+ getState()->timeDelta = 5;
+ }
+
+ TIME_CHECK_CALLBACK_2(kTimeCityVienna, CURRENT_PARAM(1, 7), 9, setup_enterStation, "Vienna", kCityVienna);
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location == kLocationOutsideTrain) {
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ } else if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ } else {
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car >= kCarBaggageRear && car <= kCarGreenSleeping) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ }
+ }
+ } else {
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ }
+ }
+ }
+
+ getSound()->resetState();
+ getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4));
+
+ ENTITY_PARAM(0, 2) = 0;
+ if (params->param1)
+ setup_viennaEvents();
+
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(5) + 20);
+ params->param2 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Chapters, viennaEvents)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventViennaAugustUnloadGuns);
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, kTime2187000, kSceneNone, true);
+ else if (getEvent(kEventCathJumpDownCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventCathJumpDownCeiling, kSceneNone, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventViennaKronosFirebird);
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else if (getEvent(kEventKronosBringEgg)) {
+ if (getEvent(kEventKronosBringEggCeiling))
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventKronosBringEggCeiling, kSceneGameOverVienna1, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna1, true);
+ } else {
+ if (getProgress().field_C0) {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeTime, getProgress().field_C0, kSceneGameOverVienna2, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna2, true);
+ } else {
+ if (getEvent(kEventKronosReturnBriefcase))
+ getLogic()->gameOver(kSavegameTypeEvent, kEventKronosReturnBriefcase, kSceneGameOverVienna, true);
+ else
+ getLogic()->gameOver(kSavegameTypeTime, kTime2155500, kSceneGameOverVienna, true);
+ }
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventVergesAnnaDead);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2250000, kSceneGameOverAnnaDied, true);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventViennaContinueGame);
+ setup_chapter4();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Chapters, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Setup for chapter 4 in case it hasn't been done before
+ if (getProgress().chapter != kChapter4) {
+ getProgress().chapter = kChapter4;
+ getEntities()->setupChapter(kChapter4);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter4;
+ getState()->timeDelta = 5;
+
+ // Save game
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!_engine->getResourceManager()->loadArchive(kArchiveCd3)) {
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+ return;
+ }
+
+ // Load scene data
+ getScenes()->loadSceneDataFile(kArchiveCd3);
+ setup_chapter4Init();
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Chapters, chapter4Init)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getSound()->processEntries();
+ getSound()->resetState();
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ getSavePoints()->push(kEntityChapters, kEntityTables0, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables1, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables2, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables3, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables4, kActionDrawTablesWithChairs);
+ getSavePoints()->push(kEntityChapters, kEntityTables5, kActionDrawTablesWithChairs);
+
+ getScenes()->loadSceneFromItemPosition(kItem3);
+
+ getInventory()->setLocationAndProcess(kItemBomb, kObjectLocation1);
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3)
+ getScenes()->loadSceneFromItemPosition(kItemBeetle);
+
+ getObjects()->updateLocation2(kObject25, kObjectLocation2);
+ getObjects()->update(kObject107, kEntityPlayer, kObjectLocation3, kCursorKeepValue, kCursorKeepValue);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 76);
+ else
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ getInventory()->show();
+ setup_chapter4Handler();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Chapters, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().isTrainRunning) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, params->param4);
+ getSound()->playLocomotiveSound();
+
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param7, getState()->timeTicks, params->param5)
+ switch (rnd(2)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityPlayer, "ZFX1008", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "ZFX1009", (SoundManager::FlagType)(rnd(15) + 2));
+ break;
+ }
+
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ params->param7 = 0;
+ UPDATE_PARAM_PROC_END
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterPoszony, params->param8, 1, setup_enterStation, "Pozsony", kCityPoszony);
+
+label_exitPozsony:
+ TIME_CHECK_CALLBACK_1(kTimeExitPoszony, CURRENT_PARAM(1, 1), 2, setup_exitStation, "Pozsony");
+
+label_enterGalanta:
+ if (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ if (getState()->time > kTime2403000 && !CURRENT_PARAM(1, 2)) {
+ CURRENT_PARAM(1, 2) = 1;
+ getProgress().field_18 = 2;
+ }
+ }
+
+ if (params->param1)
+ goto label_callback_4;
+
+ TIME_CHECK_CALLBACK_2(kTimeEnterGalanta, CURRENT_PARAM(1, 3), 3, setup_enterStation, "Galanta", kCityGalanta);
+
+label_exitGalanta:
+ TIME_CHECK_CALLBACK_1(kTimeExitGalanta, CURRENT_PARAM(1, 4), 4, setup_exitStation, "Galanta");
+
+label_callback_4:
+ if (getState()->time > kTime2470500 && !CURRENT_PARAM(1, 5)) {
+ CURRENT_PARAM(1, 5) = 1;
+
+ if (getProgress().field_18 == 2)
+ getState()->timeDelta = 1;
+ }
+
+ if (getState()->time > kTime2506500 && !CURRENT_PARAM(1, 6)) {
+ CURRENT_PARAM(1, 6) = 1;
+
+ if (getProgress().field_18 == 2)
+ getProgress().field_18 = 1;
+ }
+
+ if (getState()->time > kTime2520000 && !CURRENT_PARAM(1, 7)) {
+ CURRENT_PARAM(1, 7) = 1;
+
+ if (!params->param2 && !params->param3) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (ENTITY_PARAM(0, 2)) {
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ PLAY_STEAM();
+ break;
+ }
+
+ CarIndex car = getEntityData(kEntityPlayer)->car;
+ if (car < kCarRedSleeping || car > kCarCoalTender) {
+ if (car < kCarBaggageRear || car > kCarGreenSleeping) {
+ PLAY_STEAM();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ PLAY_STEAM();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getSound()->resetState();
+ ENTITY_PARAM(0, 3) = 0;
+ } else if (!params->param2 && !params->param3) {
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ }
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntityChapters);
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionDefault:
+ params->param4 = 225 * (4 * rnd(5) + 20);
+ params->param5 = 225 * (4 * rnd(6) + 8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_exitPozsony;
+
+ case 2:
+ goto label_enterGalanta;
+
+ case 3:
+ goto label_exitGalanta;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getSound()->processEntries();
+ getAction()->playAnimation(kEventTylerCastleDream);
+ getSound()->resetState();
+
+ getProgress().field_18 = 1;
+
+ getScenes()->loadScene(kScene41);
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+ getScenes()->loadSceneFromItemPosition(kItem22);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventTrainExplosionBridge);
+ getLogic()->gameOver(kSavegameTypeTime, kTime2430000, kSceneNone, true);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityPlayer, "MUS022");
+
+ if (getState()->time < kTime2517300)
+ getState()->time = kTime2517300;
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCathDefusingBomb);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 73);
+ break;
+
+ case 10:
+ getAction()->playAnimation(kEventDefuseBomb);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function48);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction191001984);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction191001984);
+ getScenes()->loadSceneFromItemPosition(kItem2);
+
+ getInventory()->get(kItem2)->location = kObjectLocationNone;
+ params->param2 = 1;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 2);
+ break;
+
+ case 11:
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 74);
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+ }
+ break;
+
+ case kActionChapter5:
+ setup_chapter5();
+ break;
+
+ case kAction156435676:
+ getSavePoints()->push(kEntityChapters, kEntityTatiana, kAction169360385);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ getState()->timeDelta = 1;
+ getState()->time = kTime2511900;
+
+ getInventory()->setLocationAndProcess(kItem2, kObjectLocation1);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationInsideCompartment;
+
+ getSound()->playSound(kEntityChapters, "ZFX1001");
+ break;
+
+ case kAction158610240:
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kAction169300225:
+ if (getState()->time < kTime2519100)
+ getState()->time = kTime2519100;
+
+ params->param3 = 1;
+
+ getEntities()->drawSequenceRight(kEntityChapters, "BOMB");
+ break;
+
+ case kAction190346110:
+ getProgress().field_18 = 3;
+
+ params->param1 = 1;
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getSound()->playSound(kEntityPlayer, "MUS008", SoundManager::kFlagDefault);
+ getInventory()->unselectItem();
+
+ while (getSound()->isBuffered("MUS008"))
+ getSound()->updateQueue();
+
+ if (getInventory()->hasItem(kItemBomb)) {
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function47);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function68);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function65);
+ RESET_ENTITY_STATE(kEntityMertens, Mertens, setup_function48);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function53);
+ RESET_ENTITY_STATE(kEntityServers0, Servers0, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityServers1, Servers1, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityPascale, Pascale, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityVerges, Verges, setup_chapter4Handler);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function49);
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_function44);
+ RESET_ENTITY_STATE(kEntityMilos, Milos, setup_function32);
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_function27);
+ RESET_ENTITY_STATE(kEntityIvo, Ivo, setup_function29);
+ RESET_ENTITY_STATE(kEntitySalko, Salko, setup_function22);
+ RESET_ENTITY_STATE(kEntityMmeBoutarel, MmeBoutarel, setup_function25);
+ RESET_ENTITY_STATE(kEntityBoutarel, Boutarel, setup_function35);
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function45);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_function9);
+ RESET_ENTITY_STATE(kEntityYasmin, Yasmin, setup_function17);
+ RESET_ENTITY_STATE(kEntityHadija, Hadija, setup_function19);
+ RESET_ENTITY_STATE(kEntityAlouan, Alouan, setup_function19);
+ RESET_ENTITY_STATE(kEntityMax, Max, setup_chapter4Handler);
+ getSavePoints()->push(kEntityChapters, kEntityAnna, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityMertens, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityCoudert, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers0, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityServers1, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityPascale, kAction201431954);
+ getSavePoints()->push(kEntityChapters, kEntityVerges, kAction201431954);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventTylerCastleDream);
+ } else {
+ getState()->time = kTime2520000;
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventTrainExplosionBridge);
+ }
+ break;
+
+ case kAction191001984:
+ getState()->time = kTime2520000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getEntities()->clearSequences(kEntityChapters);
+ getInventory()->removeItem(kItemTelegram);
+
+ getState()->timeDelta = 5;
+
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventDefuseBomb);
+ break;
+
+ case kAction201959744:
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSound(kEntityTrain, "ZFX4001", SoundManager::kFlagDefault);
+
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ break;
+
+ case kAction225367984:
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCathDefusingBomb);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Chapters, chapter5)
+ if (savepoint.action == kActionDefault) {
+ // Setup for chapter 5 in case it hasn't been done before
+ if (getProgress().chapter != kChapter5) {
+ getProgress().chapter = kChapter5;
+ getEntities()->setupChapter(kChapter5);
+ }
+
+ // Set game time & delta
+ getState()->time = kTimeChapter5;
+ getState()->timeDelta = 2;
+
+ setup_chapter5Init();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Chapters, chapter5Init)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTables0);
+ getEntities()->clearSequences(kEntityTables1);
+ getEntities()->clearSequences(kEntityTables2);
+ getEntities()->clearSequences(kEntityTables3);
+ getEntities()->clearSequences(kEntityTables4);
+ getEntities()->clearSequences(kEntityTables5);
+
+ getProgress().isTrainRunning = true;
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleInsideBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectKitchen, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject21, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject22, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_18 = 1;
+ getProgress().field_84 = 1;
+ getProgress().portrait = kCursorPortraitYellow;
+
+ getInventory()->unselectItem();
+
+ getInventory()->removeItem(kItemKey);
+ getInventory()->removeItem(kItemBomb);
+ getInventory()->removeItem(kItemMatch);
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->setLocationAndProcess(kItemFirebird, kObjectLocation3);
+
+ if (getInventory()->hasItem(kItemWhistle)) {
+ getInventory()->removeItem(kItemWhistle);
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation3);
+ }
+ }
+
+ getObjects()->update(kObject93, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObject101, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject98, kObjectLocation2);
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation2);
+
+ if (ENTITY_PARAM(0, 2) || ENTITY_PARAM(0, 3)) {
+ getSound()->removeFromQueue(kEntityChapters);
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 95);
+ getInventory()->show();
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter5Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Chapters, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2907000 && !params->param2) {
+ params->param2 = 1;
+
+ if (!getProgress().isNightTime) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+
+ if (getState()->time > kTimeTrainStopped2 && !params->param3) {
+ params->param3 = 1;
+
+ if (!getEvent(kEventLocomotiveMilosDay) && !getEvent(kEventLocomotiveMilosNight)) {
+ getSound()->playSound(kEntityChapters, "ARRIVE", SoundManager::kFlag8);
+ getSound()->processEntries();
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ if (getState()->time <= kTimeTrainStopped2) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTrainStopped);
+ } else {
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped2, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(10) + 20);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventTrainStopped);
+ getLogic()->gameOver(kSavegameTypeTime, kTimeTrainStopped, kSceneGameOverTrainStopped, true);
+ }
+ break;
+
+ case kAction135800432:
+ getProgress().isNightTime = true;
+ getState()->time = kTime2916000;
+
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitStation(const SavePoint &savepoint, bool isEnteringStation) {
+ if (savepoint.action == kActionDefault) {
+ if (!ENTITY_PARAM(0, 2) && !ENTITY_PARAM(0, 3)) {
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSound()->removeFromQueue(kEntityChapters);
+
+ if (!ENTITY_PARAM(0, 2)) {
+ if (ENTITY_PARAM(0, 3))
+ ENTITY_PARAM(0, 3) = 0;
+
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStopRunning);
+
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideTrain) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Green sleeping car
+ if (getEntities()->isOutsideAlexeiWindow()) {
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Red sleeping car
+ if (getEntities()->isOutsideAnnaWindow()) {
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ // Other cars
+ if (getEntityData(kEntityPlayer)->car < kCarRedSleeping || getEntityData(kEntityPlayer)->car > kCarCoalTender) {
+
+ if (getEntityData(kEntityPlayer)->car < kCarBaggageRear || getEntityData(kEntityPlayer)->car > kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 98)) {
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 71);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ return;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 82);
+ ENTITY_PARAM(0, 2) = 0;
+ enterExitHelper(isEnteringStation);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Chapters::enterExitHelper(bool isEnteringStation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ getSound()->playSound(kEntityChapters, isEnteringStation ? "ARRIVE" : "DEPART", SoundManager::kFlag8);
+ getSound()->processEntries();
+
+ getObjects()->update(kObjectHandleOutsideLeft, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+ getObjects()->update(kObjectHandleOutsideRight, kEntityPlayer, kObjectLocation1, kCursorNormal, isEnteringStation ? kCursorNormal : kCursorHand);
+
+ getProgress().isTrainRunning = isEnteringStation ? false : true;
+
+ if (isEnteringStation) {
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 4) = params->param4;
+ } else {
+ getSavePoints()->push(kEntityChapters, kEntityTrain, kActionTrainStartRunning);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ CALLBACK_ACTION();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h
new file mode 100644
index 0000000000..6a31727c23
--- /dev/null
+++ b/engines/lastexpress/entities/chapters.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_CHAPTERS_H
+#define LASTEXPRESS_CHAPTERS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Chapters : public Entity {
+public:
+ Chapters(LastExpressEngine *engine);
+ ~Chapters() {}
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ * @param index The index of the train station
+ */
+ DECLARE_FUNCTION_2(enterStation, const char *stationName, CityIndex index)
+
+ /**
+ * Exit a train station
+ *
+ * @param stationName The name of the train station
+ */
+ DECLARE_FUNCTION_1(exitStation, const char *stationName)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset main entities
+ */
+ DECLARE_FUNCTION(resetMainEntities)
+
+ /**
+ * Handle end of Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1End)
+
+ /**
+ * Init Chapter 1 data
+ */
+ DECLARE_FUNCTION(chapter1Init)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Handle switching to Chapter 2 after the end of Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1Next)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Init Chapter 2 data
+ */
+ DECLARE_FUNCTION(chapter2Init)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Init Chapter 3 data
+ */
+ DECLARE_FUNCTION(chapter3Init)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Handle Vienna events
+ */
+ DECLARE_FUNCTION(viennaEvents)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Init Chapter 4 data
+ */
+ DECLARE_FUNCTION(chapter4Init)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Init Chapter 5 data
+ */
+ DECLARE_FUNCTION(chapter5Init)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+private:
+ void enterExitStation(const SavePoint &savepoint, bool isEnteringStation);
+ void enterExitHelper(bool isEnteringStation);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_CHAPTERS_H
diff --git a/engines/lastexpress/entities/cooks.cpp b/engines/lastexpress/entities/cooks.cpp
new file mode 100644
index 0000000000..51e723887b
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.cpp
@@ -0,0 +1,571 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/cooks.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Cooks::Cooks(LastExpressEngine *engine) : Entity(engine, kEntityCooks) {
+ ADD_CALLBACK_FUNCTION(Cooks, draw);
+ ADD_CALLBACK_FUNCTION(Cooks, playSound);
+ ADD_CALLBACK_FUNCTION(Cooks, function3);
+ ADD_CALLBACK_FUNCTION(Cooks, function4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, function7);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Cooks, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Cooks, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Cooks, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Cooks, function3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kChapter1:
+ setCallback(1);
+ setup_playSound("KIT1010");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("KIT1012");
+ break;
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 46)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Cooks, function4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308A");
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+
+ case kChapter3:
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ break;
+ }
+
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer)) {
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ getEntities()->drawSequenceLeft(kEntityCooks, "308D");
+
+ if (!getSound()->isBuffered(kEntityCooks)) {
+ if (params->param1) {
+ if (!getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ // Kitchen apprentice getting a lesson :D
+ getSound()->playSound(kEntityCooks, "KIT1011A");
+ params->param1 = 1;
+ }
+ }
+
+ if (params->param1 && !getEntities()->hasValidFrame(kEntityCooks)) {
+ getSound()->playSound(kEntityCooks, "LIB015");
+ getEntities()->clearSequences(kEntityCooks);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getSound()->playSound(kEntityCooks, "KIT1011");
+ setCallback(3);
+ setup_draw("308B");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCooks, "308C");
+ getEntities()->updatePositionExit(kEntityCooks, kCarRestaurant, 75);
+ getEntities()->updatePositionEnter(kEntityCooks, kCarRestaurant, 78);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Cooks, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Cooks, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param4, getState()->time, params->param2);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 73)) {
+ setCallback(1);
+ setup_function3();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction101632192:
+ setup_function7();
+ break;
+
+ case kAction224849280:
+ getProgress().field_4C = 1;
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Cooks, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Snoring...
+ setCallback(1);
+ setup_playSound("WAT1200");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityCooks);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Cooks, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Cooks, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2)
+ params->param2 = !params->param2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Cooks, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 0;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Cooks, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param4, getState()->time, params->param2)
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ params->param4 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getState()->time > kTime2079000 && !params->param5) {
+ params->param1 = 0;
+ params->param5 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ params->param2 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ if (params->param1) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 80)) {
+ setCallback(1);
+ setup_function4();
+ }
+ } else {
+ if (params->param3) {
+ setCallback(2);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(3);
+ setup_playSound("ZFX1012");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ break;
+
+ case 2:
+ case 3:
+ params->param3 = !params->param3;
+ break;
+ }
+ break;
+
+ case kAction236976550:
+ getProgress().field_4C = 1;
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Cooks, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCooks);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getProgress().field_4C = 1;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Cooks, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param1)
+
+ // Broken plate sound
+ getSound()->playSound(kEntityPlayer, "LIB122", getSound()->getSoundFlag(kEntityCooks));
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 225 * (4 * rnd(30) + 120);
+ break;
+
+ case kActionDrawScene:
+ if (!getEntities()->isInKitchen(kEntityPlayer))
+ break;
+
+ // Kitchen background sound
+ if (params->param2) {
+ setCallback(1);
+ setup_playSound("ZFX1011");
+ } else {
+ setCallback(2);
+ setup_playSound("ZFX1012");
+ }
+ break;
+
+
+ case kActionCallback:
+ // Play the next part of background sound
+ if (getCallback() == 1 || getCallback() == 2) {
+ params->param2 = !params->param2;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Cooks, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityCooks);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/cooks.h b/engines/lastexpress/entities/cooks.h
new file mode 100644
index 0000000000..fc3a0cb78c
--- /dev/null
+++ b/engines/lastexpress/entities/cooks.h
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_COOKS_H
+#define LASTEXPRESS_COOKS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Cooks : public Entity {
+public:
+ Cooks(LastExpressEngine *engine);
+ ~Cooks() {}
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function3)
+
+ DECLARE_FUNCTION(function4)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COOKS_H
diff --git a/engines/lastexpress/entities/coudert.cpp b/engines/lastexpress/entities/coudert.cpp
new file mode 100644
index 0000000000..6dece39161
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.cpp
@@ -0,0 +1,4179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Coudert::Coudert(LastExpressEngine *engine) : Entity(engine, kEntityCoudert) {
+ ADD_CALLBACK_FUNCTION(Coudert, reset);
+ ADD_CALLBACK_FUNCTION(Coudert, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Coudert, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Coudert, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound);
+ ADD_CALLBACK_FUNCTION(Coudert, playSound16);
+ ADD_CALLBACK_FUNCTION(Coudert, savegame);
+ ADD_CALLBACK_FUNCTION(Coudert, updateEntity);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Coudert, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Coudert, excuseMe);
+ ADD_CALLBACK_FUNCTION(Coudert, function13);
+ ADD_CALLBACK_FUNCTION(Coudert, function14);
+ ADD_CALLBACK_FUNCTION(Coudert, function15);
+ ADD_CALLBACK_FUNCTION(Coudert, function16);
+ ADD_CALLBACK_FUNCTION(Coudert, function17);
+ ADD_CALLBACK_FUNCTION(Coudert, function18);
+ ADD_CALLBACK_FUNCTION(Coudert, function19);
+ ADD_CALLBACK_FUNCTION(Coudert, function20);
+ ADD_CALLBACK_FUNCTION(Coudert, function21);
+ ADD_CALLBACK_FUNCTION(Coudert, function22);
+ ADD_CALLBACK_FUNCTION(Coudert, function23);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentF);
+ ADD_CALLBACK_FUNCTION(Coudert, function25);
+ ADD_CALLBACK_FUNCTION(Coudert, function26);
+ ADD_CALLBACK_FUNCTION(Coudert, function27);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentB);
+ ADD_CALLBACK_FUNCTION(Coudert, visitCompartmentA);
+ ADD_CALLBACK_FUNCTION(Coudert, function30);
+ ADD_CALLBACK_FUNCTION(Coudert, function31);
+ ADD_CALLBACK_FUNCTION(Coudert, function32);
+ ADD_CALLBACK_FUNCTION(Coudert, function33);
+ ADD_CALLBACK_FUNCTION(Coudert, function34);
+ ADD_CALLBACK_FUNCTION(Coudert, function35);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1);
+ ADD_CALLBACK_FUNCTION(Coudert, function37);
+ ADD_CALLBACK_FUNCTION(Coudert, function38);
+ ADD_CALLBACK_FUNCTION(Coudert, function39);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function41);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter2);
+ ADD_CALLBACK_FUNCTION(Coudert, function43);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter3);
+ ADD_CALLBACK_FUNCTION(Coudert, function45);
+ ADD_CALLBACK_FUNCTION(Coudert, function46);
+ ADD_CALLBACK_FUNCTION(Coudert, function47);
+ ADD_CALLBACK_FUNCTION(Coudert, function48);
+ ADD_CALLBACK_FUNCTION(Coudert, function49);
+ ADD_CALLBACK_FUNCTION(Coudert, function50);
+ ADD_CALLBACK_FUNCTION(Coudert, function51);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter4);
+ ADD_CALLBACK_FUNCTION(Coudert, function53);
+ ADD_CALLBACK_FUNCTION(Coudert, function54);
+ ADD_CALLBACK_FUNCTION(Coudert, function55);
+ ADD_CALLBACK_FUNCTION(Coudert, function56);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5);
+ ADD_CALLBACK_FUNCTION(Coudert, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Coudert, function59);
+ ADD_CALLBACK_FUNCTION(Coudert, function60);
+ ADD_CALLBACK_FUNCTION(Coudert, function61);
+ ADD_CALLBACK_FUNCTION(Coudert, function62);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Coudert, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Coudert, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Coudert, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Coudert, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Coudert, enterExitCompartment2, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint, (EntityPosition)params->param5, (EntityPosition)params->param6, kCarRedSleeping, (ObjectIndex)params->param4, false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Coudert, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Coudert, playSound16)
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityCoudert, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Coudert, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Coudert, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 2000))
+ getData()->inventoryItem = kItemInvalid;
+ else
+ getData()->inventoryItem = kItemNone;
+
+ if (getProgress().jacket != kJacketBlood
+ || !getEntities()->isDistanceBetweenEntities(kEntityCoudert, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityPlayer, "JAC1112", getSound()->getSoundFlag(kEntityCoudert));
+ break;
+
+ case kActionExcuseMe:
+ if (getData()->clothes == kClothes1)
+ getSound()->playSound(kEntityPlayer, "ZFX1003", getSound()->getSoundFlag(kEntityCoudert));
+ else
+ getSound()->excuseMe(kEntityCoudert);
+ break;
+
+ case kActionDefault:
+ if (!getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ params->param3 = kItemInvalid;
+
+ if (getEntities()->updateEntity(kEntityCoudert, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+
+ if (getData()->direction != kDirectionUp)
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ else
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Coudert, updateFromTime, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Coudert, updateFromTicks, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters
+// - EntityIndex
+IMPLEMENT_FUNCTION_I(12, Coudert, excuseMe, EntityIndex)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale((EntityIndex)params->param1)) {
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112C" : "JAC1112F");
+ } else {
+ if (!params->param1 && getProgress().field_18 == 2) {
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, "JAC1113");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityCoudert, "JAC1113A");
+ break;
+ }
+ } else {
+ getSound()->playSound(kEntityCoudert, "JAC1112D");
+ }
+ }
+ } else {
+ if (Entities::isFemale((EntityIndex)params->param1))
+ getSound()->playSound(kEntityCoudert, Entities::isMarried((EntityIndex)params->param1) ? "JAC1112B" : "JAC1112G");
+ else
+ getSound()->playSound(kEntityCoudert, "JAC1112E");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Coudert, function13, bool, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+
+ if (!params->param4) {
+ params->param4 = getState()->timeTicks + 75;
+
+ if (!params->param4) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ if (params->param4 < getState()->timeTicks) {
+ params->param4 = kTimeInvalid;
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(4);
+ setup_function19(true);
+ break;
+ }
+ }
+
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 225);
+
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function19(true);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ ++params->param3;
+
+ setCallback(8);
+ setup_excuseMe(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ setCallback(1);
+ setup_excuseMe((EntityIndex)params->param2);
+ break;
+
+ case kAction16:
+ --params->param3;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function19(true);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(true);
+ break;
+
+ case 2:
+ if (getProgress().chapter == kChapter1 && !getProgress().eventCorpseFound && !getEvent(kEventCoudertAskTylerCompartment))
+ getData()->inventoryItem = kItemInvalid;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, params->param1 ? "667I" : "667H");
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ // BUG: the original game continues executing code here
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ CALLBACK_ACTION();
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ break;
+ }
+ break;
+
+ case kAction201439712:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Coudert, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_updateFromTime(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityCoudert, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ switch (params->param1) {
+ default:
+ break;
+
+ case kEntityVerges:
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+
+ case kEntityMmeBoutarel:
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+
+ case kEntityMertens:
+ ENTITY_PARAM(0, 5) = 0;
+ break;
+ }
+
+ setCallback(5);
+ setup_function19(false);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Coudert, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ if (params->param1)
+ getSound()->playSound(kEntityCoudert, "Tat3163");
+ else
+ getSound()->playSound(kEntityCoudert, (getProgress().chapter != kChapter3 || getState()->time > kTime1449000) ? "Tat3162A" : "Tat3161A");
+
+ setCallback(3);
+ setup_enterExitCompartment("627Xb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityTatiana, kAction69239528);
+ getData()->entityPosition = kPosition_7250;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Coudert, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(ENTITY_PARAM(0, 2) ? 1 : 2);
+ setup_bloodJacket(ENTITY_PARAM(0, 2) ? "627C" : "627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Coudert, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1) {
+ setCallback(1);
+ setup_bloodJacket("627H");
+ break;
+ }
+
+ if (params->param2) {
+ setCallback(2);
+ setup_bloodJacket("627C");
+ break;
+ }
+
+ setCallback(3);
+ setup_bloodJacket("627F");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Coudert, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 68)) {
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "JAC1111");
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_updateFromTime(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+ getSavePoints()->push(kEntityCoudert, kEntityCoudert, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(19, Coudert, function19, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 8)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7) || ENTITY_PARAM(1, 8)
+ || ENTITY_PARAM(2, 4) || ENTITY_PARAM(2, 6)) {
+ getInventory()->setLocationAndProcess(kItem5, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 4)) {
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ ENTITY_PARAM(2, 1) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param1)
+ getEntities()->drawSequenceRight(kEntityCoudert, "697H");
+ else
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(20, Coudert, function20, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 3), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityCoudert));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->time, 900);
+
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param3, (ObjectLocation)params->param4, (CursorStyle)params->param5, (CursorStyle)params->param6);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param7, (ObjectLocation)params->param8, (CursorStyle)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param7 = getObjects()->get((ObjectIndex)params->param2).entity;
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAM(1, 1) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAM(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param4 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (params->param1 == kObjectCompartmentA || params->param1 == kObjectCompartmentC
+ || params->param1 == kObjectCompartmentG || params->param1 == kObjectCompartmentH) {
+ setCallback(3);
+ setup_playSound("Jac1001B");
+ } else {
+ setCallback(4);
+ setup_playSound("Jac1001A");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getObjects()->update((ObjectIndex)params->param1, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Coudert, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vh", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wh");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("697Ah", kObjectCompartmentH);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentH, true);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentH, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("667Uh", kObjectCompartmentH);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityIvo, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("667Th", kObjectCompartmentH);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Coudert, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment("627Rg", kObjectCompartmentG);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ng");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentG, true);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentG, kObjectNone);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013A");
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(9);
+ setup_enterExitCompartment("627Ug", kObjectCompartmentG);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment("627Tg", kObjectCompartmentG);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1030");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Coudert, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vf", kObjectCompartmentF);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Coudert, visitCompartmentF)
+ visitCompartment(savepoint, kPosition_4070, "627Vf", kObjectCompartmentF, "627Wf", "627Zf", kPosition_4455, kObject53, "697Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Coudert, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(45);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction254915200);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("627Re", kObjectCompartmentE, kPosition_4840, kPosition_4455);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_function20(kObjectCompartmentE, kObject52);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Coudert, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Vd", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wd");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentD, true);
+ break;
+
+ case 3:
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentD, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(getCallback() + 1);
+ setup_function20(kObjectCompartmentD, kObject51);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("697Ad", kObjectCompartmentD);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction122865568);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+
+ setCallback(9);
+ setup_enterExitCompartment("697Ad", kObjectCompartmentD);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment2("627Zd", kObjectCompartmentD, kPosition_5790, kPosition_6130);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Coudert, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Mc", kObjectCompartmentC);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction221683008);
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Nc");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(4);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Sc", kObjectCompartmentC);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction122865568);
+ break;
+
+ case 7:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentC, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(8);
+ setup_function20(kObjectCompartmentC, kObject50);
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "JAC1013");
+
+ setCallback(9);
+ setup_enterExitCompartment("627Uc", kObjectCompartmentC);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityCoudert, kEntityBoutarel, kAction123852928);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction88652208:
+ setCallback(7);
+ setup_enterExitCompartment2("627Rc", kObjectCompartmentC, kPosition_6470, kPosition_6130);
+ break;
+
+ case kAction123199584:
+ params->param1 = 1;
+
+ setCallback(6);
+ setup_playSound("JAC1012");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Coudert, visitCompartmentB)
+ visitCompartment(savepoint, kPosition_7500, "627Vb", kObjectCompartmentB, "627Wb", "627Zb", kPosition_7850, kObject49, "697Ab");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Coudert, visitCompartmentA)
+ visitCompartment(savepoint, kPosition_8200, "627Ma", kObjectCompartmentA, "627Na", "627Ra", kPosition_7850, kObject48, "627Sa");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Coudert, function30, ObjectIndex)
+ // Expose parameters as IIIIIS and ignore the default exposed parameters
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIIS *parameters1 = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (parameters->param1) {
+ default:
+ CALLBACK_ACTION();
+ // Stop processing here
+ return;
+
+ case kObjectCompartmentA:
+ parameters->param2 = kPosition_8200;
+ parameters->param3 = kPosition_7850;
+ strcpy((char *)&parameters->seq, "627Ma");
+ strcpy((char *)&parameters1->seq1, "627Na");
+ break;
+
+ case kObjectCompartmentB:
+ parameters->param2 = kPosition_7500;
+ parameters->param3 = kPosition_7850;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vb");
+ strcpy((char *)&parameters1->seq1, "627Wb");
+ break;
+
+ case kObjectCompartmentC:
+ parameters->param2 = kPosition_6470;
+ parameters->param3 = kPosition_6130;
+ strcpy((char *)&parameters->seq, "627Mc");
+ strcpy((char *)&parameters1->seq1, "627Nc");
+ break;
+
+ case kObjectCompartmentD:
+ parameters->param2 = kPosition_5790;
+ parameters->param3 = kPosition_6130;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vd");
+ strcpy((char *)&parameters1->seq1, "627Wd");
+ break;
+
+ case kObjectCompartmentE:
+ parameters->param2 = kPosition_4840;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Me");
+ strcpy((char *)&parameters1->seq1, "627Ne");
+ break;
+
+ case kObjectCompartmentF:
+ parameters->param2 = kPosition_4070;
+ parameters->param3 = kPosition_4455;
+ parameters->param4 = true;
+ strcpy((char *)&parameters->seq, "627Vf");
+ strcpy((char *)&parameters1->seq1, "627Wf");
+ break;
+ }
+
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, (EntityPosition)parameters->param2);
+ break;
+
+ case 2:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, (EntityPosition)parameters->param3)
+ || ((parameters->param1 == kObjectCompartmentE || parameters->param1 == kObjectCompartmentF) && getEntities()->isOutsideAnnaWindow())) {
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorNormal, kCursorNormal);
+ parameters->param5 = true;
+ }
+
+ setCallback(3);
+ setup_enterExitCompartment((char *)&parameters->seq, (ObjectIndex)parameters->param1);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, (char *)&parameters1->seq1);
+ getEntities()->enterCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(4);
+ setup_playSound(parameters->param4 ? "JAC3020" : "JAC3021");
+ break;
+
+ case 4:
+ if (parameters->param5)
+ getObjects()->update((ObjectIndex)parameters->param1, kEntityPlayer, getObjects()->get((ObjectIndex)parameters->param1).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityCoudert, (ObjectIndex)parameters->param1, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function18();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Coudert, function31, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function19(true);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("627G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ } else {
+ setCallback(2);
+ setup_function19(true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Coudert, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Coudert, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5) || ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)
+ || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 6) = 1;
+
+ if (ENTITY_PARAM(0, 3) || ENTITY_PARAM(0, 4) || ENTITY_PARAM(0, 5)) {
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ } else {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ }
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function14(kEntityMertens);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_updateFromTime(75);
+ break;
+
+ case 6:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ setCallback(7);
+ setup_function37();
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(8);
+ setup_function39();
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function55();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(10);
+ setup_function34(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ ENTITY_PARAM(2, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(34, Coudert, function34, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ if (!params->param1) {
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("Jac1001");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityCoudert, "Ann3125");
+
+ setCallback(5);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+
+ setCallback(7);
+ setup_function35((bool)params->param1);
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(35, Coudert, function35, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage)) {
+ getAction()->playAnimation(kEventCoudertBaggageCar);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ UPDATE_PARAM(params->param2, getState()->time, 2700);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage);
+
+ getData()->clothes = kClothesDefault;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionDefault:
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction156049968);
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kAction122358304);
+
+ getData()->clothes = kClothes1;
+ getData()->entityPosition = kPosition_4370;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, "Ann3124");
+
+ if (params->param1)
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction123733488);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityCoudert);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function18();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Coudert, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTimeChapter1, params->param1, 1, setup_chapter1Handler)
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityCoudert, kAction292048641, 7);
+ getSavePoints()->addData(kEntityCoudert, kAction326348944, 8);
+ getSavePoints()->addData(kEntityCoudert, kAction171394341, 2);
+ getSavePoints()->addData(kEntityCoudert, kAction154005632, 4);
+ getSavePoints()->addData(kEntityCoudert, kAction169557824, 3);
+ getSavePoints()->addData(kEntityCoudert, kAction226031488, 5);
+ getSavePoints()->addData(kEntityCoudert, kAction339669520, 6);
+ getSavePoints()->addData(kEntityCoudert, kAction189750912, 10);
+ getSavePoints()->addData(kEntityCoudert, kAction185737168, 12);
+ getSavePoints()->addData(kEntityCoudert, kAction185671840, 13);
+ getSavePoints()->addData(kEntityCoudert, kAction205033696, 15);
+ getSavePoints()->addData(kEntityCoudert, kAction157026693, 14);
+ getSavePoints()->addData(kEntityCoudert, kAction189026624, 11);
+ getSavePoints()->addData(kEntityCoudert, kAction168254872, 17);
+ getSavePoints()->addData(kEntityCoudert, kAction201431954, 18);
+ getSavePoints()->addData(kEntityCoudert, kAction188570113, 19);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_chapter1Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Coudert, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getSound()->isBuffered(kEntityCoudert))
+ getSound()->processEntry(kEntityCoudert);
+
+ if (ENTITY_PARAM(0, 7)) {
+ getData()->entityPosition = kPosition_8200;
+
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ } else {
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction238358920);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("698Ha", kObjectCompartmentA, kPosition_8200, kPosition_7850);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+ setup_function38();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Coudert, function38)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_chapter1Handler();
+ break;
+ }
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Coudert, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("627Vd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wd");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(5);
+ setup_playSound("MME1151A");
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentD, true);
+
+ setCallback(6);
+ setup_enterExitCompartment("627Zd", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(7);
+ setup_playSound("MME1151");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("MME1151", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction223068211);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function18();
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction167854368);
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Coudert, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ getData()->inventoryItem = (getProgress().eventCorpseFound || getEvent(kEventCoudertAskTylerCompartment)) ? kItemNone : kItemInvalid;
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(1, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(5);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function37();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_6:
+ if (ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(7);
+ setup_function14(kEntityMertens);
+ break;
+ }
+
+label_callback_7:
+ if (ENTITY_PARAM(0, 4)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+
+label_callback_8:
+ if (ENTITY_PARAM(2, 2)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(9);
+ setup_function39();
+ break;
+ }
+
+label_callback_9:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "JAC1065" : "JAC1065A");
+
+ if (getState()->time > kTime1107000 && !ENTITY_PARAM(0, 1) && !getEvent(kEventVassiliSeizure)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(10);
+ setup_function41();
+ break;
+ }
+
+label_callback_10:
+ if (getState()->time > kTime1189800 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 2) = 1;
+ ENTITY_PARAM(0, 1) = 1;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!ENTITY_PARAM(0, 2))
+ break;
+
+ TIME_CHECK_OBJECT(kTime1107000, params->param4, kObject111, kObjectLocation2);
+ TIME_CHECK_OBJECT(kTime1161000, params->param5, kObject111, kObjectLocation3);
+ TIME_CHECK_OBJECT(kTime1206000, params->param6, kObject111, kObjectLocation4);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertAskTylerCompartment);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(13);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 1) && !getEntities()->isPlayerPosition(kCarRedSleeping, 23))
+ break;
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCoudertBloodJacket);
+ } else {
+ setCallback(getEntities()->isPlayerPosition(kCarRedSleeping, 1) ? 2 : 3);
+ setup_function13(true, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ params->param1 = 1;
+ goto label_callback_10;
+
+ case 11:
+ getAction()->playAnimation(kEventCoudertAskTylerCompartment);
+ getEntities()->drawSequenceRight(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627A" : "627D");
+ getScenes()->loadSceneFromItemPosition(kItem5);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 25);
+
+ setCallback(12);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityCoudert, ENTITY_PARAM(0, 2) ? "627B" : "627E");
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction168253822:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityCoudert, "JAC1120");
+
+ setCallback(14);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityCoudert, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Coudert, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_visitCompartmentA();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_visitCompartmentB();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function27();
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction285528346);
+
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function26();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function25();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function23();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_function22();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function33();
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function21();
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_function18();
+ break;
+
+ case 18:
+ getSavePoints()->push(kEntityCoudert, kEntityMilos, kAction208228224);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Coudert, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation5);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function43();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Coudert, function43)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function15(true);
+ break;
+ }
+
+label_callback1:
+ if (!ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_function15(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function14(kEntityVerges);
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(4);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(5);
+ setup_function13(true, kEntityPlayer);
+
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(6);
+ setup_function13(false, kEntityPlayer);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 7:
+ setCallback(8);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(7);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Coudert, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation6);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Coudert, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(4);
+ setup_function14(kEntityMertens);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(1, 3)) {
+ setCallback(5);
+ setup_function46();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(6);
+ setup_function47(true);
+ break;
+ }
+
+label_callback_6:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(7);
+ setup_function47(false);
+ break;
+ }
+
+label_callback_7:
+ if (ENTITY_PARAM(1, 8)) {
+ setCallback(8);
+ setup_function48();
+ break;
+ }
+
+label_callback_8:
+ if (ENTITY_PARAM(2, 4)) {
+ setCallback(9);
+ setup_function49();
+ break;
+ }
+
+label_callback_9:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(10);
+ setup_function34(true);
+ break;
+ }
+
+label_callback_10:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(11);
+ setup_function34(false);
+ break;
+ }
+
+label_callback_11:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(12);
+ setup_function14(kEntityMmeBoutarel);
+ break;
+ }
+
+label_callback_12:
+ // BUG: Can never be called... FAIL!
+ if (ENTITY_PARAM(2, 5) && getState()->time > kTime2056500 && getState()->time < kTime1417500) {
+ setCallback(13);
+ setup_function50();
+ break;
+ }
+
+label_callback_13:
+ TIME_CHECK_CALLBACK(kTime2088900, params->param1, 14, setup_function32);
+
+label_callback_14:
+ TIME_CHECK_CALLBACK(kTime2119500, params->param2, 15, setup_function32);
+
+label_callback_15:
+ TIME_CHECK_CALLBACK(kTime2138400, params->param3, 16, setup_function32);
+
+label_callback_16:
+ TIME_CHECK_CALLBACK(kTime2147400, params->param4, 17, setup_function32);
+
+label_callback_17:
+ TIME_CHECK_CALLBACK(kTime2160000, params->param5, 18, setup_function32);
+
+label_callback_18:
+ TIME_CHECK_CALLBACK(kTime2205000, params->param6, 19, setup_function32);
+
+label_callback_19:
+ if (ENTITY_PARAM(0, 2)) {
+ TIME_CHECK_OBJECT(kTime2025000, params->param7, kObject111, kObjectLocation7);
+ TIME_CHECK_OBJECT(kTime2133000, params->param8, kObject111, kObjectLocation8);
+ TIME_CHECK_OBJECT(kTime2173500, CURRENT_PARAM(1, 1), kObject111, kObjectLocation9);
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(20);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(21);
+ setup_function13(true, kEntityPlayer);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(22);
+ setup_function13(false, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction168255788);
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+
+ case 15:
+ goto label_callback_15;
+
+ case 16:
+ goto label_callback_16;
+
+ case 17:
+ goto label_callback_17;
+
+ case 18:
+ goto label_callback_18;
+
+ case 19:
+ goto label_callback_19;
+
+ case 23:
+ setCallback(24);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(25);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(23);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(26);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Coudert, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Vf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction253868128);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+
+ setCallback(4);
+ setup_playSound("Ann1016A");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("Ann4150");
+ break;
+
+ case 5:
+ getSound()->playSound(kEntityCoudert, "Ann3121");
+
+ setCallback(6);
+ setup_enterExitCompartment("629Bf", kObjectCompartmentF);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "629Cf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ // Fallback to next case
+
+ case 7:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(7);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(8);
+ setup_playSound("Ann3122");
+ }
+ break;
+
+ case 8:
+ getSound()->playSound(kEntityCoudert, "Ann3123");
+
+ setCallback(9);
+ setup_updateFromTicks(75);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("629Ff", kObjectCompartmentF);
+ break;
+
+ case 10:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ ENTITY_PARAM(1, 3) = 0;
+
+ setCallback(11);
+ setup_function35(true);
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(47, Coudert, function47, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF);
+ // Fallback to next case
+
+ case 4:
+ if (getSound()->isBuffered(kEntityCoudert)) {
+ setCallback(4);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(5);
+ setup_playSound(params->param1 ? "Ann3149" : "Ann3147a");
+ }
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction157894320);
+
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Coudert, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityCoudert, "Ann3148A");
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Ann3148B" : "Ann3148");
+ setCallback(3);
+ setup_enterExitCompartment("627Xf", kObjectCompartmentF);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction192063264);
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ ENTITY_PARAM(1, 8) = 0;
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Coudert, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("627Vb", kObjectCompartmentB);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityTatiana, kCarRedSleeping, kPosition_7500)) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+
+ setCallback(4);
+ setup_playSound("Jac3006");
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(8);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(5);
+ setup_enterExitCompartment("627Zb", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3006A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("697Ab", kObjectCompartmentB);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(150);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityCoudert, kEntityMmeBoutarel, kAction242526416);
+ ENTITY_PARAM(2, 4) = 0;
+ ENTITY_PARAM(2, 5) = 1;
+
+ setCallback(11);
+ setup_function18();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Coudert, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Me");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ if (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840)) {
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+
+ setCallback(4);
+ setup_playSound("Jac3005");
+ }
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(6);
+ setup_playSound("Jac3005A");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ ENTITY_PARAM(2, 5) = 0;
+
+ setCallback(9);
+ setup_function18();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Coudert, function51)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2133000 && !getProgress().field_40) {
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (savepoint.param.intValue == kObjectCompartmentB)
+ getData()->entityPosition = kPosition_7500;
+
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventCoudertGoingOutOfVassiliCompartment);
+ getEntities()->updateEntity(kEntityCoudert, kCarRedSleeping, kPosition_2000);
+ getScenes()->loadSceneFromObject(savepoint.param.intValue == kObjectCompartmentB ? kObjectCompartmentB : kObjectCompartmentA);
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityCoudert, kEntityMax, kActionMaxFreeFromCage);
+
+ if (ENTITY_PARAM(0, 5)) {
+ ENTITY_PARAM(0, 5) = 0;
+
+ getSavePoints()->push(kEntityCoudert, kEntityMertens, kAction155853632);
+ getSavePoints()->push(kEntityCoudert, kEntityMertens, kActionEndSound);
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ ENTITY_PARAM(0, 3) = 0;
+
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kAction155853632);
+ getSavePoints()->push(kEntityCoudert, kEntityVerges, kActionEndSound);
+ }
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wb");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getSavePoints()->push(kEntityCoudert, kEntityTatiana, kAction154071333);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ case 4:
+ case 6:
+ setup_function45();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function18();
+ break;
+
+ case 5:
+ setCallback(5);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction168316032:
+ getObjects()->update(kObjectCompartmentA, kEntityCoudert, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorHand);
+ break;
+
+ case kAction235061888:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Coudert, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+
+ getObjects()->updateLocation2(kObject111, kObjectLocation10);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ ENTITY_PARAM(1, 2) = 1;
+ setup_function53();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Coudert, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ params->param1 = 1;
+
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentB, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentC, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentD, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentE, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentF, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentG, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartmentH, kObjectLocation1);
+
+ ENTITY_PARAM(2, 3) = 0;
+
+ setCallback(1);
+ setup_function54();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 2)) {
+ if (!params->param2)
+ params->param2 = (uint)(getState()->time + 4500);
+
+ if (params->param3 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param2, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param3, 0)
+ setCallback(2);
+ setup_function55();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(3);
+ setup_function34(false);
+ break;
+ }
+
+label_callback_3:
+ if (!params->param1) {
+ TIME_CHECK_CALLBACK(kTime2394000, params->param4, 4, setup_function56);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK(kTime2434500, params->param5, 5, setup_function32);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2448000, params->param6, 6, setup_function32);
+ }
+
+label_callback_6:
+ if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM(params->param7, getState()->time, 2700);
+
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 1) = 1;
+
+ getEntities()->drawSequenceLeft(kEntityCoudert, "697F");
+
+ params->param7 = 0;
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(7);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem5);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(8);
+ setup_function13(true, kEntityPlayer);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ setCallback(9);
+ setup_function13(false, kEntityPlayer);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 10:
+ setCallback(11);
+ setup_function18();
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(12);
+ setup_function30((ObjectIndex)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getSound()->playSound(kEntityCoudert, "JAC2020");
+
+ setCallback(10);
+ setup_bloodJacket("697D");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(13);
+ setup_function31(savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Coudert, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->hasValidFrame(kEntityCoudert)) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ getData()->car = kCarLocomotive;
+ getData()->entityPosition = kPosition_540;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityCoudert);
+ getData()->car = kCarLocomotive;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction191001984:
+ getData()->car = kCarRedSleeping;
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Coudert, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_playSound("LIB070");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function16();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Wf");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_playSound("Ann4150A");
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityCoudert, kEntityAnna, kAction219971920);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction101824388);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction136059947);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function18();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(56, Coudert, function56)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function21();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function22();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_visitCompartmentF();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function25();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function33();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function26();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function33();
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_function27();
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function33();
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_visitCompartmentB();
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_function18();
+ break;
+
+ case 16:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(57, Coudert, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityCoudert);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(58, Coudert, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function59();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(59, Coudert, function59)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSound()->playSound(kEntityCoudert, "Jac5010"); // Situation is under control, please remain in your compartment
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627K");
+ setup_function60();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(60, Coudert, function60)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function61();
+ break;
+
+ case kAction155991520:
+ setCallback(1);
+ setup_updateFromTime(225);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(61, Coudert, function61)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("627Me", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, "627Ne");
+ getEntities()->enterCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityCoudert, kObjectCompartmentE, true);
+
+ setCallback(4);
+ setup_enterExitCompartment("627Re", kObjectCompartmentE);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_playSound("Reb5010");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("627Se", kObjectCompartmentE);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityCoudert, kEntityRebecca, kAction155604840);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("627Zh", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityCoudert);
+ getSavePoints()->push(kEntityCoudert, kEntityPascale, kAction169750080);
+
+ setup_function62();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(62, Coudert, function62)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ ++params->param3;
+
+ if (params->param3 == 1 || params->param2) {
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 4 : 5);
+ setup_playSound(params->param3 == 1 ? "Jac5002" : "Jac5002A");
+ }
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentH, kEntityCoudert, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 5:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(63, Coudert)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Coudert::visitCompartment(const SavePoint &savepoint, EntityPosition position, const char *seq1, ObjectIndex compartment, const char *seq2, const char *seq3, EntityPosition sittingPosition, ObjectIndex object, const char *seq4) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, position);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment(seq1, compartment);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityCoudert, seq2);
+ getEntities()->enterCompartment(kEntityCoudert, compartment, true);
+
+ setCallback(3);
+ setup_updateFromTime(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2(seq3, compartment, position, sittingPosition);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityCoudert, compartment, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityCoudert);
+
+ setCallback(5);
+ setup_function20(compartment, object);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment(seq4, compartment);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/coudert.h b/engines/lastexpress/entities/coudert.h
new file mode 100644
index 0000000000..13dad6f122
--- /dev/null
+++ b/engines/lastexpress/entities/coudert.h
@@ -0,0 +1,229 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_COUDERT_H
+#define LASTEXPRESS_COUDERT_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Coudert : public Entity {
+public:
+ Coudert(LastExpressEngine *engine);
+ ~Coudert() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position 1
+ * @param entityPosition2 The entity position 2
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char *sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ DECLARE_FUNCTION_1(excuseMe, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, EntityIndex entity)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION_1(function19, bool)
+
+ /**
+ * ???
+ *
+ * @param object1 The first object index
+ * @param object2 The second object index
+ */
+ DECLARE_FUNCTION_2(function20, ObjectIndex object1, ObjectIndex object2)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(visitCompartmentF)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(visitCompartmentB)
+ DECLARE_FUNCTION(visitCompartmentA)
+
+ /**
+ * ???
+ *
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_1(function30, ObjectIndex compartment)
+
+ DECLARE_FUNCTION_1(function31, uint32)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION_1(function34, bool)
+ DECLARE_FUNCTION_1(function35, bool)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function43)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION_1(function47, bool)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function53)
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+ DECLARE_FUNCTION(function56)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function59)
+ DECLARE_FUNCTION(function60)
+ DECLARE_FUNCTION(function61)
+ DECLARE_FUNCTION(function62)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void visitCompartment(const SavePoint &savepoint, EntityPosition position, const char *seq1, ObjectIndex compartment, const char *seq2, const char *seq3, EntityPosition sittingPosition, ObjectIndex object, const char *seq4);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_COUDERT_H
diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp
new file mode 100644
index 0000000000..3291b49e9b
--- /dev/null
+++ b/engines/lastexpress/entities/entity.cpp
@@ -0,0 +1,486 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// EntityData
+//////////////////////////////////////////////////////////////////////////
+
+void EntityData::EntityCallData::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(callbacks); i++)
+ s.syncAsByte(callbacks[i]);
+
+ s.syncAsByte(currentCall);
+ s.syncAsUint16LE(entityPosition);
+ s.syncAsUint16LE(location);
+ s.syncAsUint16LE(car);
+ s.syncAsByte(field_497);
+ s.syncAsByte(entity);
+ s.syncAsByte(inventoryItem);
+ s.syncAsByte(direction);
+ s.syncAsUint16LE(field_49B);
+ s.syncAsUint16LE(currentFrame);
+ s.syncAsUint16LE(currentFrame2);
+ s.syncAsUint16LE(field_4A1);
+ s.syncAsUint16LE(field_4A3);
+ s.syncAsByte(clothes);
+ s.syncAsByte(position);
+ s.syncAsByte(car2);
+ s.syncAsByte(doProcessEntity);
+ s.syncAsByte(field_4A9);
+ s.syncAsByte(field_4AA);
+ s.syncAsByte(directionSwitch);
+
+ // Sync strings
+#define SYNC_STRING(varName, count) { \
+ char seqName[13]; \
+ memset(&seqName, 0, count); \
+ if (s.isSaving()) strcpy((char *)&seqName, varName.c_str()); \
+ s.syncBytes((byte *)&seqName, count); \
+ if (s.isLoading()) varName = seqName; \
+}
+
+ SYNC_STRING(sequenceName, 13);
+ SYNC_STRING(sequenceName2, 13);
+ SYNC_STRING(sequenceNamePrefix, 7);
+ SYNC_STRING(sequenceNameCopy, 13);
+
+#undef SYNC_STRING
+
+ // Skip pointers to frame & sequences
+ s.skip(5 * 4);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// EntityData
+//////////////////////////////////////////////////////////////////////////
+EntityData::EntityParameters *EntityData::getParameters(uint callback, byte index) const {
+ if (callback >= 9)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 9)", callback);
+
+ if (index >= 4)
+ error("EntityData::getParameters: invalid index value (was: %d, max: 4)", index);
+
+ return _parameters[callback].parameters[index];
+}
+
+int EntityData::getCallback(uint callback) const {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ return _data.callbacks[callback];
+}
+
+void EntityData::setCallback(uint callback, byte index) {
+ if (callback >= 16)
+ error("EntityData::getParameters: invalid callback value (was: %d, max: 16)", callback);
+
+ _data.callbacks[callback] = index;
+}
+
+void EntityData::updateParameters(uint32 index) const {
+ if (index < 8)
+ getParameters(8, 0)->update(index);
+ else if (index < 16)
+ getParameters(8, 1)->update(index - 8);
+ else if (index < 24)
+ getParameters(8, 2)->update(index - 16);
+ else if (index < 32)
+ getParameters(8, 3)->update(index - 24);
+ else
+ error("EntityData::updateParameters: invalid param index to update (was:%d, max:32)!", index);
+}
+
+void EntityData::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(_parameters); i++)
+ _parameters[i].saveLoadWithSerializer(s);
+
+ _data.saveLoadWithSerializer(s);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entity
+//////////////////////////////////////////////////////////////////////////
+Entity::Entity(LastExpressEngine *engine, EntityIndex index) : _engine(engine), _entityIndex(index) {
+ _data = new EntityData();
+
+ // Add first empty entry to callbacks array
+ _callbacks.push_back(NULL);
+}
+
+Entity::~Entity() {
+ for (uint i = 0; i < _callbacks.size(); i++)
+ delete _callbacks[i];
+
+ delete _data;
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+void Entity::setup(ChapterIndex index) {
+ switch(index) {
+ case kChapterAll:
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]);
+ break;
+
+ case kChapter1:
+ setup_chapter1();
+ break;
+
+ case kChapter2:
+ setup_chapter2();
+ break;
+
+ case kChapter3:
+ setup_chapter3();
+ break;
+
+ case kChapter4:
+ setup_chapter4();
+ break;
+
+ case kChapter5:
+ setup_chapter5();
+ break;
+
+ default:
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared functions
+//////////////////////////////////////////////////////////////////////////
+
+void Entity::reset(const SavePoint &savepoint, bool resetClothes, bool resetItem) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction1:
+ if (resetClothes) {
+ // Select next available clothes
+ getData()->clothes = (ClothesIndex)(getData()->clothes + 1);
+ if (getData()->clothes > kClothes3)
+ getData()->clothes = kClothesDefault;
+ }
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, kCarGreenSleeping, (EntityPosition)params->param1))
+ params->param1 = (params->param1 == 10000) ? 0 : 10000;
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ if (resetItem)
+ getData()->inventoryItem = kItemInvalid;
+
+ params->param1 = 10000;
+ break;
+ }
+}
+
+void Entity::savegame(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSaveLoad()->saveGame((SavegameType)params->param1, _entityIndex, (EventIndex)params->param2);
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::playSound(const SavePoint &savepoint, bool resetItem, SoundManager::FlagType flag) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (resetItem)
+ getData()->inventoryItem = kItemNone;
+
+ getSound()->playSound(_entityIndex, (char *)&params->seq1, flag);
+ break;
+ }
+}
+
+void Entity::draw(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param4) {
+ getSound()->excuseMe(_entityIndex);
+ params->param4 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::draw2(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->drawSequenceRight((EntityIndex)params->param7, (char *)&params->seq2);
+ break;
+ }
+}
+
+void Entity::updateFromTicks(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->timeTicks, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateFromTime(const SavePoint &savepoint) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionOnDirection(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callbackActionRestaurantOrSalon(const SavePoint &savepoint) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon())
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::updateEntity(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe)
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ if (handleExcuseMe)
+ getSound()->excuseMe(_entityIndex);
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+void Entity::callSavepoint(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ if (!CURRENT_PARAM(1, 1))
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !CURRENT_PARAM(1, 2)) {
+ getSound()->excuseMe(_entityIndex);
+ CURRENT_PARAM(1, 2) = 1;
+ }
+ break;
+
+ case kAction10:
+ if (!CURRENT_PARAM(1, 1)) {
+ getSavePoints()->call(_entityIndex, (EntityIndex)params->param4, (ActionIndex)params->param5, (char *)&params->seq2);
+ CURRENT_PARAM(1, 1) = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ break;
+ }
+}
+
+void Entity::enterExitCompartment(const SavePoint &savepoint, EntityPosition position1, EntityPosition position2, CarIndex car, ObjectIndex compartment, bool alternate, bool updateLocation) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ if (position1)
+ getData()->entityPosition = position1;
+
+ if (updateLocation)
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq1);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+
+ if (position1) {
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, car, position1) || getEntities()->isInsideCompartment(kEntityPlayer, car, position2)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(compartment, alternate);
+ }
+ }
+ break;
+ }
+}
+
+void Entity::updatePosition(const SavePoint &savepoint, bool handleExcuseMe) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII)
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (handleExcuseMe && !params->param6) {
+ getSound()->excuseMe(_entityIndex);
+ params->param6 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->updatePositionEnter(_entityIndex, (CarIndex)params->param4, (Position)params->param5);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h
new file mode 100644
index 0000000000..3e12df8612
--- /dev/null
+++ b/engines/lastexpress/entities/entity.h
@@ -0,0 +1,800 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ENTITY_H
+#define LASTEXPRESS_ENTITY_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/sound.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+struct SavePoint;
+
+class EntityData : Common::Serializable {
+public:
+
+ struct EntityParameters : Common::Serializable{
+ virtual ~EntityParameters() {}
+ virtual Common::String toString() = 0;
+
+ virtual void update(uint32 index) = 0;
+
+ virtual void saveLoadWithSerializer(Common::Serializer &s) = 0;
+ };
+
+ struct EntityParametersIIII : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIIII() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ bool hasNonNullParameter() {
+ return param1 || param2 || param3 || param4 || param5 || param6 || param7 || param8;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IIII: %d %d %d %d %d %d %d %d\n", param1, param2, param3, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSIII : EntityParameters {
+ char seq[12];
+ uint param4;
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersSIII() {
+ memset(&seq, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SIII: %s %d %d %d %d %d\n", seq, param4, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIII::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSIIS : EntityParameters {
+ char seq1[12];
+ uint param4;
+ uint param5;
+ char seq2[12];
+
+ EntityParametersSIIS() {
+ memset(&seq1, 0, 12);
+ param4 = 0;
+ param5 = 0;
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SIIS: %s %d %d %s\n", seq1, param4, param5, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSIIS::update: invalid index (was: %d)", index);
+
+ case 3: param4 = 1; break;
+ case 4: param5 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncBytes((byte *)&seq2, 12);
+ }
+ };
+
+ struct EntityParametersISSI : EntityParameters {
+ uint param1;
+ char seq1[12];
+ char seq2[12];
+ uint param8;
+
+ EntityParametersISSI() {
+ param1 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("ISSI: %d %s %s %d\n", param1, seq1, seq2, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISSI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersISII : EntityParameters {
+ uint param1;
+ char seq[12];
+ uint param5;
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersISII() {
+ param1 = 0;
+ memset(&seq, 0, 12);
+ param5 = 0;
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("ISII: %d %s %d %d %d %d\n", param1, seq, param5, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersISII::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 4: param5 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param5);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSSII : EntityParameters {
+ char seq1[12];
+ char seq2[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersSSII() {
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SSII: %s %s %d %d\n", seq1, seq2, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersSSII::update: invalid index (was: %d)", index);
+
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersSSS : EntityParameters {
+ char seq1[12];
+ char seq2[12];
+ char seq3[8];
+
+ EntityParametersSSS() {
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ memset(&seq3, 0, 8);
+ }
+
+ Common::String toString() {
+ return Common::String::printf("SSS: %s %s %s\n", seq1, seq2, seq3);
+ }
+
+ void update(uint32) {
+ error("EntityParametersSSS::update: cannot update this type of parameters");
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ s.syncBytes((byte *)&seq3, 8);
+ }
+ };
+
+ struct EntityParametersIISS : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq1[12];
+ char seq2[12];
+
+ EntityParametersIISS() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq1, 0, 12);
+ memset(&seq2, 0, 12);
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IISS: %d %d %s %s\n", param1, param2, seq1, seq2);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncBytes((byte *)&seq1, 12);
+ s.syncBytes((byte *)&seq2, 12);
+ }
+ };
+
+ struct EntityParametersIISI : EntityParameters {
+ uint param1;
+ uint param2;
+ char seq[12];
+ uint param6;
+ uint param7;
+ uint param8;
+
+ EntityParametersIISI() {
+ param1 = 0;
+ param2 = 0;
+ memset(&seq, 0, 12);
+ param6 = 0;
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IISI: %d %d %s %d %d %d\n", param1, param2, seq, param6, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIISI::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 5: param6 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param6);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersIIIS : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ char seq[12];
+ uint param7;
+ uint param8;
+
+ EntityParametersIIIS() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ memset(&seq, 0, 12);
+ param7 = 0;
+ param8 = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("IIIS: %d %d %d %s %d %d\n", param1, param2, param3, seq, param7, param8);
+ }
+
+ void update(uint32 index) {
+ switch (index) {
+ default:
+ error("EntityParametersIIIS::update: invalid index (was: %d)", index);
+
+ case 0: param1 = 1; break;
+ case 1: param2 = 1; break;
+ case 2: param3 = 1; break;
+ case 6: param7 = 1; break;
+ case 7: param8 = 1; break;
+ }
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncBytes((byte *)&seq, 12);
+ s.syncAsUint32LE(param7);
+ s.syncAsUint32LE(param8);
+ }
+ };
+
+ struct EntityParametersI5S : EntityParameters {
+ uint param1;
+ uint param2;
+ uint param3;
+ uint param4;
+ uint param5;
+ char seq[12];
+
+ EntityParametersI5S() {
+ param1 = 0;
+ param2 = 0;
+ param3 = 0;
+ param4 = 0;
+ param5 = 0;
+ memset(&seq, 0, 12);
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(param1);
+ s.syncAsUint32LE(param2);
+ s.syncAsUint32LE(param3);
+ s.syncAsUint32LE(param4);
+ s.syncAsUint32LE(param5);
+ s.syncBytes((byte *)&seq, 12);
+ }
+ };
+
+ struct EntityCallParameters : Common::Serializable {
+ EntityParameters *parameters[4];
+
+ EntityCallParameters() {
+ // We default to int parameters
+ for (int i = 0; i < 4; i++)
+ parameters[i] = new EntityParametersIIII();
+ }
+
+ ~EntityCallParameters() {
+ clear();
+ }
+
+ void clear() {
+ for (int i = 0; i < 4; i++) {
+ if (parameters[i])
+ delete parameters[i];
+ parameters[i] = NULL;
+ }
+ }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(parameters); i++)
+ parameters[i]->saveLoadWithSerializer(s);
+ }
+ };
+
+ struct EntityCallData : Common::Serializable {
+ byte callbacks[16];
+ byte currentCall;
+ EntityPosition entityPosition; // word
+ Location location; // word
+ CarIndex car; // word
+ byte field_497;
+ EntityIndex entity; // byte
+ InventoryItem inventoryItem; // byte
+ EntityDirection direction; // byte
+ int16 field_49B;
+ int16 currentFrame;
+ int16 currentFrame2;
+ int16 field_4A1;
+ int16 field_4A3;
+ ClothesIndex clothes; // byte
+ Position position;
+ CarIndex car2; // byte
+ bool doProcessEntity; // byte
+ bool field_4A9; // byte
+ bool field_4AA; // byte
+ EntityDirection directionSwitch;
+ Common::String sequenceName; // char[13]
+ Common::String sequenceName2; // char[13]
+ Common::String sequenceNamePrefix; // char[7]
+ Common::String sequenceNameCopy; // char[13]
+ SequenceFrame *frame;
+ SequenceFrame *frame1;
+ Sequence *sequence;
+ Sequence *sequence2;
+ Sequence *sequence3;
+
+ /**
+ * Default constructor.
+ */
+ EntityCallData() {
+ memset(&callbacks, 0, 16 * sizeof(byte));
+ currentCall = 0;
+ entityPosition = kPositionNone;
+ location = kLocationOutsideCompartment;
+ car = kCarNone;
+ field_497 = 0;
+ entity = kEntityPlayer;
+ inventoryItem = kItemNone;
+ direction = kDirectionNone;
+ field_49B = 0;
+ currentFrame = 0;
+ currentFrame2 = 0;
+ field_4A1 = 0;
+ field_4A3 = 30;
+ clothes = kClothesDefault;
+ position = 0;
+ car2 = kCarNone;
+ doProcessEntity = false;
+ field_4A9 = false;
+ field_4AA = false;
+ directionSwitch = kDirectionNone;
+ frame = NULL;
+ frame1 = NULL;
+ sequence = NULL;
+ sequence2 = NULL;
+ sequence3 = NULL;
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String str = "";
+
+ str += Common::String::printf("Entity position: %d - Location: %d - Car: %d\n", entityPosition, location, car);
+ str += Common::String::printf("Entity: %d - Item: %d - Direction: %d\n", entity, inventoryItem, direction);
+ str += Common::String::printf("Clothes: %d - Position: %d - Direction switch: %d\n", clothes, position, directionSwitch);
+ str += "\n";
+ str += Common::String::printf("field_497: %02d - field_49B: %i - field_4A1: %i\n", field_497, field_49B, field_4A1);
+ str += Common::String::printf("field_4A9: %02d - field_4AA: %i - Car 2: %d\n", field_4A9, field_4AA, car2);
+ str += "\n";
+ str += "Sequence: " + sequenceName + " - Sequence 2: " + sequenceName2 + "\n";
+ str += "Sequence prefix: " + sequenceNamePrefix + " - Sequence copy: " + sequenceNameCopy + "\n";
+ str += Common::String::printf("Current frame: %i - Current frame 2: %i - Process entity: %d\n", currentFrame, currentFrame2, doProcessEntity);
+ str += "\n";
+ str += Common::String::printf("Current call: %d\n", currentCall);
+ str += Common::String::printf("Functions: %d %d %d %d %d %d %d %d\n", callbacks[0], callbacks[1], callbacks[2], callbacks[3], callbacks[4], callbacks[5], callbacks[6], callbacks[7]);
+ str += Common::String::printf("Callbacks: %d %d %d %d %d %d %d %d\n", callbacks[8], callbacks[9], callbacks[10], callbacks[11], callbacks[12], callbacks[13], callbacks[14], callbacks[15]);
+
+ return str;
+ }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+ };
+
+ EntityData() {}
+
+ template<class T>
+ void resetCurrentParameters() {
+ EntityCallParameters *params = &_parameters[_data.currentCall];
+ params->clear();
+
+ for (int i = 0; i < 4; i++)
+ params->parameters[i] = new T();
+ }
+
+ EntityCallData *getCallData() { return &_data; }
+
+ EntityParameters *getParameters(uint callback, byte index) const;
+ EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); }
+
+ int getCallback(uint callback) const;
+ int getCurrentCallback() { return getCallback(_data.currentCall); }
+ void setCallback(uint callback, byte index);
+ void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); }
+
+ void updateParameters(uint32 index) const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+
+ EntityCallData _data;
+ EntityCallParameters _parameters[9];
+};
+
+class Entity : Common::Serializable {
+public:
+
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+ Entity(LastExpressEngine *engine, EntityIndex index);
+ virtual ~Entity();
+
+ // Accessors
+ EntityData *getParamData() { return _data; }
+ EntityData::EntityCallData *getData() { return _data->getCallData(); }
+
+ // Callbacks
+ byte getCallback() { return _data->getCallback(_data->getCallData()->currentCall + 8); }
+ void setCallback(byte index) { _data->setCallback(_data->getCallData()->currentCall + 8, index); getData()->currentCall++; }
+
+ // Setup
+ void setup(ChapterIndex index);
+
+ virtual void setup_chapter1() = 0;
+ virtual void setup_chapter2() = 0;
+ virtual void setup_chapter3() = 0;
+ virtual void setup_chapter4() = 0;
+ virtual void setup_chapter5() = 0;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser) { _data->saveLoadWithSerializer(ser); }
+
+ void nullfunction(const SavePoint &savepoint) {}
+
+protected:
+ LastExpressEngine *_engine;
+
+ EntityIndex _entityIndex;
+ EntityData *_data;
+ Common::Array<Callback *> _callbacks;
+
+ /**
+ * Saves the game
+ *
+ * @param savepoint The savepoint
+ * - SavegameType
+ * - EventIndex
+ */
+ void savegame(const SavePoint &savepoint);
+
+ /**
+ * Play sound
+ *
+ * @param savepoint The savepoint
+ * - Sound filename
+ * @param resetItem true to reset item.
+ * @param flag sound flag
+ */
+ void playSound(const SavePoint &savepoint, bool resetItem = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid);
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - Sequence
+ * - ExcuseMe flag
+ * @param handleExcuseMe true to handle excuseMeCath action
+ */
+ void draw(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint.
+ * - Sequence 1
+ * - Sequence 2
+ * - EntityIndex
+ */
+ void draw2(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - Number of ticks to add
+ */
+ void updateFromTicks(const SavePoint &savepoint);
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint.
+ * - Time to add
+ */
+ void updateFromTime(const SavePoint &savepoint);
+
+ /**
+ * Resets an entity
+ *
+ * @param savepoint The savepoint.
+ * @param resetClothes true to reset clothes.
+ * @param resetItem true to reset inventoryItem to kItemInvalid
+ */
+ void reset(const SavePoint &savepoint, bool resetClothes = false, bool resetItem = false);
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionOnDirection(const SavePoint &savepoint);
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ *
+ * @param savepoint The savepoint.
+ */
+ void callbackActionRestaurantOrSalon(const SavePoint &savepoint);
+
+ /**
+ * Updates the entity
+ *
+ * @param savepoint The savepoint.
+ * - CarIndex
+ * - EntityPosition
+ * @param handleExcuseMe true to handle the kActionExcuseMe/kActionExcuseMeCath actions.
+ */
+ void updateEntity(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Call a specific savepoint (or draw sequence in default case)
+ *
+ * @param savepoint The savepoint.
+ * - Sequence to draw in default case
+ * - EntityIndex
+ * - ActionIndex
+ * - Sequence for the savepoint
+ * @param handleExcuseMe true to handle excuse me.
+ */
+ void callSavepoint(const SavePoint &savepoint, bool handleExcuseMe = false);
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param savepoint The savepoint.
+ * @param position1 The first position.
+ * @param position2 The second position.
+ * @param car The car.
+ * @param compartment The compartment.
+ * @param alternate true to use the alternate version of SceneManager::loadSceneFromObject()
+ */
+ void enterExitCompartment(const SavePoint &savepoint, EntityPosition position1 = kPositionNone, EntityPosition position2 = kPositionNone, CarIndex car = kCarNone, ObjectIndex compartment = kObjectNone, bool alternate = false, bool updateLocation = false);
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - Sequence name
+ * - CarIndex
+ * - Position
+ * @param handleExcuseMe true to handle excuseMe actions
+ */
+ void updatePosition(const SavePoint &savepoint, bool handleExcuseMe = false);
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_H
diff --git a/engines/lastexpress/entities/entity39.cpp b/engines/lastexpress/entities/entity39.cpp
new file mode 100644
index 0000000000..6d139094c7
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.cpp
@@ -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$
+ *
+ */
+
+#include "lastexpress/entities/entity39.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Entity39::Entity39(LastExpressEngine *engine) : Entity(engine, kEntity39) {
+ ADD_CALLBACK_FUNCTION(Entity39, chapter1);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter2);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter3);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter4);
+ ADD_CALLBACK_FUNCTION(Entity39, chapter5);
+ ADD_CALLBACK_FUNCTION(Entity39, process);
+
+ memset(&_sequence, 0, 12);
+ _counter = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Entity39, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Entity39, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Entity39, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Entity39, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Entity39, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Entity39, process)
+// TODO: _sequence & counter do not seem to be touched anywhere else in the code :(
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ break;
+
+ case kActionNone:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ if (*_sequence && !_counter) {
+ _counter++;
+ getEntities()->drawSequenceRight(kEntity39, (char *)&_sequence);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/entity39.h b/engines/lastexpress/entities/entity39.h
new file mode 100644
index 0000000000..344cf5b494
--- /dev/null
+++ b/engines/lastexpress/entities/entity39.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_ENTITY39_H
+#define LASTEXPRESS_ENTITY39_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Entity39 : public Entity {
+public:
+ Entity39(LastExpressEngine *engine);
+ ~Entity39() {}
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Process function
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ char _sequence[12];
+ int _counter;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_##define##_H
diff --git a/engines/lastexpress/entities/entity_intern.h b/engines/lastexpress/entities/entity_intern.h
new file mode 100644
index 0000000000..71607ed511
--- /dev/null
+++ b/engines/lastexpress/entities/entity_intern.h
@@ -0,0 +1,528 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ENTITY_INTERN_H
+#define LASTEXPRESS_ENTITY_INTERN_H
+
+namespace LastExpress {
+
+#define LOW_BYTE(w) ((unsigned char)(((unsigned long)(w)) & 0xff))
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+#define ENTITY_CALLBACK(class, name, pointer) \
+ Common::Functor1Mem<const SavePoint&, void, class>(pointer, &class::name)
+
+#define ADD_CALLBACK_FUNCTION(class, name) \
+ _callbacks.push_back(new ENTITY_CALLBACK(class, name, this));
+
+#define ADD_NULL_FUNCTION() \
+ _callbacks.push_back(new ENTITY_CALLBACK(Entity, nullfunction, this));
+
+//////////////////////////////////////////////////////////////////////////
+// Declaration
+//////////////////////////////////////////////////////////////////////////
+
+#define DECLARE_FUNCTION(name) \
+ void setup_##name(); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_1(name, param1) \
+ void setup_##name(param1); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_2(name, param1, param2) \
+ void setup_##name(param1, param2); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_3(name, param1, param2, param3) \
+ void setup_##name(param1, param2, param3); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_4(name, param1, param2, param3, param4) \
+ void setup_##name(param1, param2, param3, param4); \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_FUNCTION_NOSETUP(name) \
+ void name(const SavePoint &savepoint);
+
+#define DECLARE_NULL_FUNCTION() \
+ void setup_nullfunction();
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+#define IMPLEMENT_SETUP(class, callback_class, name, index) \
+void class::setup_##name() { \
+ BEGIN_SETUP(callback_class, name, index, EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::setup_" #name "()"); \
+ END_SETUP() \
+}
+
+#define BEGIN_SETUP(class, name, index, type) \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->setCallback(_entityIndex, new ENTITY_CALLBACK(class, name, this)); \
+ _data->setCurrentCallback(index); \
+ _data->resetCurrentParameters<type>();
+
+#define END_SETUP() \
+ _engine->getGameLogic()->getGameState()->getGameSavePoints()->call(_entityIndex, _entityIndex, kActionDefault);
+
+
+//////////////////////////////////////////////////////////////////////////
+// Implementation
+//////////////////////////////////////////////////////////////////////////
+
+// Expose parameters and check validity
+#define EXPOSE_PARAMS(type) \
+ type *params = (type*)_data->getCurrentParameters(); \
+ if (!params) \
+ error("Trying to call an entity function with invalid parameters!"); \
+
+
+// function signature without setup (we keep the index for consistency but never use it)
+#define IMPLEMENT_FUNCTION_NOSETUP(index, class, name) \
+ void class::name(const SavePoint &savepoint) { \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(index=" #index ")");
+
+// simple setup with no parameters
+#define IMPLEMENT_FUNCTION(index, class, name) \
+ IMPLEMENT_SETUP(class, class, name, index) \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "() - action: %s", ACTION_NAME(savepoint.action));
+
+// nullfunction call
+#define IMPLEMENT_NULL_FUNCTION(index, class) \
+ IMPLEMENT_SETUP(class, Entity, nullfunction, index)
+
+// setup with one uint parameter
+#define IMPLEMENT_FUNCTION_I(index, class, name, paramType) \
+ void class::setup_##name(paramType param1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d) - action: %s", params->param1, ACTION_NAME(savepoint.action));
+
+// setup with two uint parameters
+#define IMPLEMENT_FUNCTION_II(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d) - action: %s", params->param1, params->param2, ACTION_NAME(savepoint.action));
+
+// setup with three uint parameters
+#define IMPLEMENT_FUNCTION_III(index, class, name, paramType1, paramType2, paramType3) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, paramType3 param3) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIIII) \
+ EntityData::EntityParametersIIII *params = (EntityData::EntityParametersIIII*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ params->param3 = param3; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %d) - action: %s", params->param1, params->param2, params->param3, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter
+#define IMPLEMENT_FUNCTION_S(index, class, name) \
+ void class::setup_##name(const char *seq1) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s) - action: %s", (char *)&params->seq1, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and one uint
+#define IMPLEMENT_FUNCTION_SI(index, class, name, paramType2) \
+ void class::setup_##name(const char *seq1, paramType2 param4) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d) - action: %s", (char *)&params->seq1, params->param4, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and two uints
+#define IMPLEMENT_FUNCTION_SII(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d) - action: %s", (char *)&params->seq1, params->param4, params->param5, ACTION_NAME(savepoint.action));
+
+// setup with one char *parameter and three uints
+#define IMPLEMENT_FUNCTION_SIII(index, class, name, paramType2, paramType3, paramType4) \
+ void class::setup_##name(const char *seq, paramType2 param4, paramType3 param5, paramType4 param6) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIII) \
+ EntityData::EntityParametersSIII *params = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq, seq, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ params->param6 = param6; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %d) - action: %s", (char *)&params->seq, params->param4, params->param5, params->param6, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SIIS(index, class, name, paramType2, paramType3) \
+ void class::setup_##name(const char *seq1, paramType2 param4, paramType3 param5, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSIIS) \
+ EntityData::EntityParametersSIIS *params = (EntityData::EntityParametersSIIS*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ params->param4 = param4; \
+ params->param5 = param5; \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %d, %d, %s) - action: %s", (char *)&params->seq1, params->param4, params->param5, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SS(index, class, name) \
+ void class::setup_##name(const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s) - action: %s", (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_SSI(index, class, name, paramType3) \
+ void class::setup_##name(const char *seq1, const char *seq2, paramType3 param7) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersSSII) \
+ EntityData::EntityParametersSSII *params = (EntityData::EntityParametersSSII*)_data->getCurrentParameters(); \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ params->param7 = param7; \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersSSII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%s, %s, %d) - action: %s", (char *)&params->seq1, (char *)&params->seq2, params->param7, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char *seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISII) \
+ EntityData::EntityParametersISII *params = (EntityData::EntityParametersISII*)_data->getCurrentParameters(); \
+ params->param1 = (unsigned int)param1; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISII) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s) - action: %s", params->param1, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_ISS(index, class, name, paramType) \
+ void class::setup_##name(paramType param1, const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersISSI) \
+ EntityData::EntityParametersISSI *params = (EntityData::EntityParametersISSI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersISSI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %s, %s) - action: %s", params->param1, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IIS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISI) \
+ EntityData::EntityParametersIISI *params = (EntityData::EntityParametersIISI*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq, seq, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISI) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s) - action: %s", params->param1, params->param2, (char *)&params->seq, ACTION_NAME(savepoint.action));
+
+#define IMPLEMENT_FUNCTION_IISS(index, class, name, paramType1, paramType2) \
+ void class::setup_##name(paramType1 param1, paramType2 param2, const char *seq1, const char *seq2) { \
+ BEGIN_SETUP(class, name, index, EntityData::EntityParametersIISS) \
+ EntityData::EntityParametersIISS *params = (EntityData::EntityParametersIISS*)_data->getCurrentParameters(); \
+ params->param1 = param1; \
+ params->param2 = param2; \
+ strncpy((char *)&params->seq1, seq1, 12); \
+ strncpy((char *)&params->seq2, seq2, 12); \
+ END_SETUP() \
+ } \
+ void class::name(const SavePoint &savepoint) { \
+ EXPOSE_PARAMS(EntityData::EntityParametersIISS) \
+ debugC(6, kLastExpressDebugLogic, "Entity: " #class "::" #name "(%d, %d, %s, %s) - action: %s", params->param1, params->param2, (char *)&params->seq1, (char *)&params->seq2, ACTION_NAME(savepoint.action));
+
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+#define RESET_ENTITY_STATE(entity, class, function) \
+ getEntities()->resetState(entity); \
+ ((class*)getEntities()->get(entity))->function();
+
+//////////////////////////////////////////////////////////////////////////
+// Parameters macros (for default IIII parameters)
+//////////////////////////////////////////////////////////////////////////
+#define CURRENT_PARAM(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getCurrentParameters(index))->param##id
+
+#define ENTITY_PARAM(index, id) \
+ ((EntityData::EntityParametersIIII*)_data->getParameters(8, index))->param##id
+
+//////////////////////////////////////////////////////////////////////////
+// Time check macros
+//////////////////////////////////////////////////////////////////////////
+#define TIME_CHECK(timeValue, parameter, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_SAVEPOINT(timeValue, parameter, entity1, entity2, action) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSavePoints()->push(entity1, entity2, action); \
+ }
+
+#define TIME_CHECK_CALLBACK(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_1(timeValue, parameter, callback, function, param1) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_2(timeValue, parameter, callback, function, param1, param2) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_3(timeValue, parameter, callback, function, param1, param2, param3) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ setCallback(callback); \
+ function(param1, param2, param3); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_INVENTORY(timeValue, parameter, callback, function) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->inventoryItem = kItemNone; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+#define TIME_CHECK_CALLBACK_ACTION(timeValue, parameter) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ CALLBACK_ACTION(); \
+ break; \
+ }
+
+#define TIME_CHECK_PLAYSOUND_UPDATEPOSITION(timeValue, parameter, callback, sound, position) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getData()->entityPosition = position; \
+ setCallback(callback); \
+ setup_playSound(sound); \
+ break; \
+ }
+
+#define TIME_CHECK_OBJECT(timeValue, parameter, object, location) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getObjects()->updateLocation2(object, location); \
+ }
+
+#define TIME_CHECK_CAR(timeValue, parameter, callback, function) {\
+ if ((getState()->time <= timeValue && !getEntities()->isPlayerInCar(kCarGreenSleeping)) || !parameter) \
+ parameter = (uint)getState()->time + 75; \
+ if (getState()->time > timeValue || parameter < getState()->time) { \
+ parameter = kTimeInvalid; \
+ setCallback(callback); \
+ function(); \
+ break; \
+ } \
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callback action
+//////////////////////////////////////////////////////////////////////////
+#define CALLBACK_ACTION() { \
+ if (getData()->currentCall == 0) \
+ error("CALLBACK_ACTION: currentCall is already 0, cannot proceed!"); \
+ getData()->currentCall--; \
+ getSavePoints()->setCallback(_entityIndex, _callbacks[_data->getCurrentCallback()]); \
+ getSavePoints()->call(_entityIndex, _entityIndex, kActionCallback); \
+ }
+
+//////////////////////////////////////////////////////////////////////////
+// Param update
+//////////////////////////////////////////////////////////////////////////
+#define UPDATE_PARAM(parameter, type, value) { \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter >= type) \
+ break; \
+ parameter = kTimeInvalid; \
+}
+
+// Todo: replace with UPDATE_PARAM_PROC as appropriate
+#define UPDATE_PARAM_GOTO(parameter, type, value, label) { \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter >= type) \
+ goto label; \
+ parameter = kTimeInvalid; \
+}
+
+// Updating parameter with code inside the check
+#define UPDATE_PARAM_PROC(parameter, type, value) \
+ if (!parameter) \
+ parameter = (uint)(type + value); \
+ if (parameter < type) { \
+ parameter = kTimeInvalid;
+
+#define UPDATE_PARAM_PROC_TIME(timeValue, test, parameter, value) \
+ if (getState()->time <= timeValue) { \
+ if (test || !parameter) \
+ parameter = (uint)(getState()->time + value); \
+ } \
+ if (parameter < getState()->time || getState()->time > timeValue) { \
+ parameter = kTimeInvalid;
+
+#define UPDATE_PARAM_PROC_END }
+
+// Updating parameter with an added check (and code inside the check)
+#define UPDATE_PARAM_CHECK(parameter, type, value) \
+ if (!parameter || parameter < type) { \
+ if (!parameter) \
+ parameter = (uint)(type + value);
+
+//////////////////////////////////////////////////////////////////////////
+// Compartments
+//////////////////////////////////////////////////////////////////////////
+// Go from one compartment to another (or the same one if no optional args are passed
+#define COMPARTMENT_TO(class, compartmentFrom, positionFrom, sequenceFrom, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_enterExitCompartment(sequenceTo, compartmentFrom); \
+ break; \
+ case 2: \
+ getData()->entityPosition = positionFrom; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ } \
+ break; \
+ }
+
+#define COMPARTMENT_FROM_TO(class, compartmentFrom, positionFrom, sequenceFrom, compartmentTo, positionTo, sequenceTo) \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionDefault: \
+ getData()->entityPosition = positionFrom; \
+ getData()->location = kLocationOutsideCompartment; \
+ setCallback(1); \
+ setup_enterExitCompartment(sequenceFrom, compartmentFrom); \
+ break; \
+ case kActionCallback: \
+ switch (getCallback()) { \
+ default: \
+ break; \
+ case 1: \
+ setCallback(2); \
+ setup_updateEntity(kCarGreenSleeping, positionTo); \
+ break; \
+ case 2: \
+ setCallback(3); \
+ setup_enterExitCompartment(sequenceTo, compartmentTo); \
+ break; \
+ case 3: \
+ getData()->location = kLocationInsideCompartment; \
+ getEntities()->clearSequences(_entityIndex); \
+ CALLBACK_ACTION(); \
+ break; \
+ } \
+ break; \
+ }
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITY_INTERN_H
diff --git a/engines/lastexpress/entities/francois.cpp b/engines/lastexpress/entities/francois.cpp
new file mode 100644
index 0000000000..bec164e116
--- /dev/null
+++ b/engines/lastexpress/entities/francois.cpp
@@ -0,0 +1,1295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/francois.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Francois::Francois(LastExpressEngine *engine) : Entity(engine, kEntityFrancois) {
+ ADD_CALLBACK_FUNCTION(Francois, reset);
+ ADD_CALLBACK_FUNCTION(Francois, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Francois, draw);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Francois, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Francois, playSound);
+ ADD_CALLBACK_FUNCTION(Francois, savegame);
+ ADD_CALLBACK_FUNCTION(Francois, updateEntity);
+ ADD_CALLBACK_FUNCTION(Francois, function9);
+ ADD_CALLBACK_FUNCTION(Francois, function10);
+ ADD_CALLBACK_FUNCTION(Francois, function11);
+ ADD_CALLBACK_FUNCTION(Francois, function12);
+ ADD_CALLBACK_FUNCTION(Francois, function13);
+ ADD_CALLBACK_FUNCTION(Francois, function14);
+ ADD_CALLBACK_FUNCTION(Francois, function15);
+ ADD_CALLBACK_FUNCTION(Francois, function16);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1);
+ ADD_CALLBACK_FUNCTION(Francois, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function19);
+ ADD_CALLBACK_FUNCTION(Francois, function20);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2);
+ ADD_CALLBACK_FUNCTION(Francois, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function23);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3);
+ ADD_CALLBACK_FUNCTION(Francois, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4);
+ ADD_CALLBACK_FUNCTION(Francois, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5);
+ ADD_CALLBACK_FUNCTION(Francois, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Francois, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Francois, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Francois, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Francois, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Francois, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Francois, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Francois, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Francois, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Francois, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000)
+ || !getInventory()->hasItem(kItemFirebird)
+ || getEvent(kEventFrancoisShowEgg)
+ || getEvent(kEventFrancoisShowEggD)
+ || getEvent(kEventFrancoisShowEggNight)
+ || getEvent(kEventFrancoisShowEggNightD)) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000)
+ && getInventory()->get(kItemBeetle)->location == kObjectLocation1
+ && !getEvent(kEventFrancoisShowBeetle)
+ && !getEvent(kEventFrancoisShowBeetleD))
+ getData()->inventoryItem = kItemMatchBox;
+ } else {
+ getData()->inventoryItem = kItemFirebird;
+ }
+
+ if (ENTITY_PARAM(0, 1)
+ && getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 1000)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisTradeWhistle);
+ }
+ }
+ break;
+
+ case kAction1:
+ switch (savepoint.param.intValue) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisShowBeetle);
+ break;
+
+ case 18:
+ if (isNight())
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowEggNightD : kEventFrancoisShowEggNight);
+ else
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowEggD : kEventFrancoisShowEgg);
+
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ getSound()->excuseMe(_entityIndex);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisTradeWhistleD : kEventFrancoisTradeWhistle);
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->removeItem(kItemMatchBox);
+ getInventory()->get(kItemBeetle)->location = kObjectLocation2;
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ ENTITY_PARAM(0, 1) = 0;
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisShowBeetleD : kEventFrancoisShowBeetle);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionUp ? -1 : 1))), getData()->direction == kDirectionUp);
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Francois, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction134289824);
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Ed", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Francois, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getObjects()->get(kObjectCompartmentD).location == kObjectLocation2) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ setCallback(1);
+ setup_enterExitCompartment("605Bd", kObjectCompartmentD);
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("605Dd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction102484312);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Francois, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityFrancois)) {
+
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->timeTicks, params->param6)
+ switch (rnd(7)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityFrancois, "Fra1002A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityFrancois, "Fra1002B");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityFrancois, "Fra1002C");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityFrancois, "Fra1002D");
+ break;
+
+ case 4:
+ getSound()->playSound(kEntityFrancois, "Fra1002E");
+ break;
+
+ case 5:
+ case 6:
+ getSound()->playSound(kEntityFrancois, "Fra1002F");
+ break;
+ }
+
+ params->param6 = 15 * rnd(7);
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->hasValidFrame(kEntityFrancois) || !getEntities()->isWalkingOppositeToPlayer(kEntityFrancois))
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->updateEntity(kEntityFrancois, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ params->param5 = 0;
+
+ if (params->param3 == kPosition_540) {
+ params->param2 = (getProgress().chapter == kChapter1) ? kCarRedSleeping : kCarGreenSleeping;
+ params->param3 = kPosition_9460;
+ } else {
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+ params->param7 = 0;
+ params->param8 = 0;
+
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction225932896);
+ getSavePoints()->push(kEntityFrancois, kEntityMertens, kAction225932896);
+ }
+ }
+
+ if (getEntities()->checkDistanceFromPosition(kEntityFrancois, kPosition_2000, 500) && getData()->direction == kDirectionDown) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping) && params->param8) {
+ setCallback(2);
+ setup_draw("605A");
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarGreenSleeping) && params->param7) {
+ setCallback(3);
+ setup_draw("605A");
+ break;
+ }
+ }
+
+label_callback:
+ if (getProgress().chapter == kChapter1) {
+
+ if (getEntities()->isInsideTrainCar(kEntityFrancois, kCarRedSleeping)
+ && (getEntities()->hasValidFrame(kEntityFrancois) || params->param1 < getState()->time || params->param4)
+ && !params->param5
+ && getData()->entityPosition < getEntityData(kEntityMmeBoutarel)->entityPosition) {
+
+ if (getData()->direction == kDirectionDown) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction202221040);
+ params->param4 = 1;
+ params->param5 = 1;
+ } else if (params->param4 && getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityMmeBoutarel, 1000)) {
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction168986720);
+ params->param5 = 1;
+ }
+ }
+ } else if (params->param1 < getState()->time) {
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(kEntityFrancois))
+ getSound()->processEntry(kEntityFrancois);
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventFrancoisWhistle);
+ break;
+
+ case kActionExcuseMeCath:
+ if (getProgress().jacket == kJacketGreen
+ && !getEvent(kEventFrancoisWhistle)
+ && !getEvent(kEventFrancoisWhistleD)
+ && !getEvent(kEventFrancoisWhistleNight)
+ && !getEvent(kEventFrancoisWhistleNightD))
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothes1;
+ getData()->field_4A3 = 100;
+ getData()->inventoryItem = kItemNone;
+
+ params->param2 = kCarGreenSleeping;
+ params->param3 = kPosition_540;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarGreenSleeping, kPosition_540);
+
+ params->param6 = 15 * rnd(7);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityFrancois, kEntityCoudert, kAction168253822);
+ // Fallback to next case
+
+ case 3:
+ params->param2 = kCarRedSleeping;
+ params->param3 = kPosition_9460;
+ params->param5 = 0;
+
+ getData()->entityPosition = kPosition_2088;
+
+ getEntities()->updateEntity(kEntityFrancois, kCarRedSleeping, kPosition_9460);
+ goto label_callback;
+
+ case 4:
+ setCallback(5);
+ setup_function10();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ if (getProgress().jacket == kJacketGreen) {
+ if (isNight())
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleNightD : kEventFrancoisWhistleNight);
+ else
+ getAction()->playAnimation(getData()->entityPosition <= getEntityData(kEntityPlayer)->entityPosition ? kEventFrancoisWhistleD : kEventFrancoisWhistleD);
+ }
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750 * (getData()->direction == kDirectionUp ? -1 : 1)), getData()->direction == kDirectionUp);
+ break;
+ }
+ break;
+
+ case kAction102752636:
+ getEntities()->clearSequences(kEntityFrancois);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ getData()->clothes = kClothesDefault;
+ getData()->field_4A3 = 30;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction205346192:
+ if (savepoint.entity2 == kEntityCoudert)
+ params->param8 = 1;
+ else if (savepoint.entity2 == kEntityMertens)
+ params->param7 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Francois, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(675);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateFromTime(675);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function10();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Francois, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Df", kObjectCompartment6);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+
+ setCallback(5);
+ setup_playSound("Har2010");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityFrancois, kEntityAlouan, kAction189489753);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 7:
+ if (getInventory()->hasItem(kItemWhistle) || getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityFrancois, "605He");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateFromTime(450);
+ break;
+
+ case 9:
+ getEntities()->exitCompartment(kEntityFrancois, kObjectCompartmentE, true);
+
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function10();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction190219584:
+ setCallback(6);
+ setup_enterExitCompartment("605Ef", kObjectCompartment6);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(14, Francois, function14, ObjectIndex, EntityPosition)
+ // Expose parameters as IISS and ignore the default exposed parameters
+ EntityData::EntityParametersIISS *parameters = (EntityData::EntityParametersIISS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ strcpy((char *)&parameters->seq2, "605H");
+ strcat((char *)&parameters->seq2, (char *)&parameters->seq1);
+
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, (EntityPosition)parameters->param2);
+ break;
+
+ case 2:
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ getEntities()->drawSequenceLeft(kEntityFrancois, (char *)&parameters->seq2);
+ getEntities()->enterCompartment(kEntityFrancois, (ObjectIndex)parameters->param1, true);
+
+ setCallback(3);
+ setup_playSound("Fra2005A");
+ } else {
+ if (parameters->param2 >= kPosition_5790) {
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ }
+ }
+ break;
+
+ case 3:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_updateFromTime(rnd(450));
+ break;
+
+ case 4:
+ case 6:
+ setCallback(getCallback() + 1);
+ setup_playSound(rnd(2) ? "Fra2005B" : "Fra2005C");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(rnd(150));
+ break;
+
+ case 8:
+ getEntities()->exitCompartment(kEntityFrancois, (ObjectIndex)parameters->param1);
+ // Fallback to next case
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_updateFromTime(900);
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function10();
+ break;
+
+ case 13:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Francois, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function9();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getData()->entityPosition >= getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_540);
+ } else {
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ }
+ break;
+
+ case 2:
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function10();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ if (!getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(8);
+ setup_playSound("Fra2012");
+ break;
+
+ case 8:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Francois, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_enterExitCompartment("605Cd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_5890;
+
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction101107728);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityFrancois);
+ getSavePoints()->push(kEntityFrancois, kEntityBoutarel, kAction237889408);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Francois, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Francois, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTimeParisEpernay, params->param1, 1, setup_function11, kTime1093500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function19();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Francois, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param1, 2, setup_function12);
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Francois, function20)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityFrancois);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Francois, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Francois, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("605Id", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getSavePoints()->push(kEntityFrancois, kEntityMmeBoutarel, kAction100957716);
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityFrancois);
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Francois, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventFrancoisShowBeetle) || getEvent(kEventFrancoisShowBeetleD))
+ if (!getEvent(kEventFrancoisTradeWhistle) && !getEvent(kEventFrancoisTradeWhistleD))
+ ENTITY_PARAM(0, 1) = 1;
+
+ if (ENTITY_PARAM(0, 1) && getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ setCallback(1);
+ setup_function15();
+ break;
+ }
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTime1764000, params->param1, 2, setup_playSound, "Fra2011");
+
+label_callback_2:
+ TIME_CHECK_CALLBACK(kTime1800000, params->param2, 3, setup_function13);
+
+label_callback_3:
+ if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ TIME_CHECK_CALLBACK_1(kTime1768500, params->param3, 4, setup_function11, kTime1773000);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime1827000, params->param4, 5, setup_function11, kTime1831500);
+ }
+
+label_callback_5:
+ if (getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ // TODO: do we also need to check if the whistle is in the inventory?
+ break;
+ }
+
+ if (params->param5 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTimeEnd, !getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000), params->param5, 75);
+ setCallback(6);
+ setup_playSound("Fra2010");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_3(kTime1782000, params->param6, 7, setup_function14, kObjectCompartmentC, kPosition_6470, "c");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_3(kTime1813500, params->param7, 8, setup_function14, kObjectCompartmentF, kPosition_4070, "f");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ getProgress().field_9C = 1;
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Francois, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Francois, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventFrancoisShowBeetle) || getEvent(kEventFrancoisShowBeetleD))
+ if (!getEvent(kEventFrancoisTradeWhistle) && !getEvent(kEventFrancoisTradeWhistleD))
+ ENTITY_PARAM(0, 1) = 1;
+
+ if (params->param2 && getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790) && !params->param1) {
+
+ if (ENTITY_PARAM(0, 1) && getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ setCallback(2);
+ setup_function15();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK(kTime2025000, params->param3, 3, setup_function12);
+
+label_callback_3:
+ TIME_CHECK_CALLBACK(kTime2052000, params->param4, 4, setup_function12);
+
+label_callback_4:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param5, 5, setup_function12);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2092500, params->param6, 6, setup_function12);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2173500, params->param7, 7, setup_function12);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2182500, params->param8, 8, setup_function12);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK(kTime2241000, CURRENT_PARAM(1, 1), 9, setup_function12);
+
+label_callback_9:
+ if (!getInventory()->hasItem(kItemWhistle) && getInventory()->get(kItemWhistle)->location != kObjectLocation3) {
+ TIME_CHECK_CALLBACK_1(kTime2011500, CURRENT_PARAM(1, 2), 10, setup_function11, kTime2016000);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK_1(kTime2115000, CURRENT_PARAM(1, 3), 11, setup_function11, kTime2119500);
+ }
+
+label_callback_11:
+ if (getInventory()->get(kItemWhistle)->location == kObjectLocation3) {
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isDistanceBetweenEntities(kEntityFrancois, kEntityPlayer, 2000) || !params->param4)
+ params->param4 = (uint)(getState()->time + 75);
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ setCallback(12);
+ setup_playSound("Fra2010");
+ break;
+ }
+
+label_callback_12:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 5), 13, setup_function14, kObjectCompartmentE, kPosition_4840, "e");
+
+label_callback_13:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 6), 14, setup_function14, kObjectCompartmentF, kPosition_4070, "f");
+
+label_callback_14:
+ TIME_CHECK_CALLBACK_3(kTime2040300, CURRENT_PARAM(1, 7), 15, setup_function14, kObjectCompartmentB, kPosition_7500, "b");
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param2 = 1;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ getProgress().field_9C = 1;
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(1);
+ setup_function16();
+ break;
+
+ case kAction189872836:
+ params->param1 = 1;
+ break;
+ case kAction190390860:
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Francois, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Francois, chapter4Handler)
+ if (savepoint.action == kAction101107728) {
+ setCallback(1);
+ setup_function16();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Francois, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityFrancois);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Francois, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5) {
+ if (!getInventory()->hasItem(kItemWhistle)
+ && getInventory()->get(kItemWhistle)->location != kObjectLocation3)
+ getInventory()->setLocationAndProcess(kItemWhistle, kObjectLocation1);
+
+ setup_function30();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Francois, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Francois)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/francois.h b/engines/lastexpress/entities/francois.h
new file mode 100644
index 0000000000..c924cf677b
--- /dev/null
+++ b/engines/lastexpress/entities/francois.h
@@ -0,0 +1,170 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_FRANCOIS_H
+#define LASTEXPRESS_FRANCOIS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Francois : public Entity {
+public:
+ Francois(LastExpressEngine *engine);
+ ~Francois() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION_3(function14, ObjectIndex compartment, EntityPosition entityPosition, const char *str)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FRANCOIS_H
diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp
new file mode 100644
index 0000000000..620a885a60
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.cpp
@@ -0,0 +1,620 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/gendarmes.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Gendarmes::Gendarmes(LastExpressEngine *engine) : Entity(engine, kEntityGendarmes) {
+ ADD_CALLBACK_FUNCTION(Gendarmes, reset);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestDraw);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestPlaysound16);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestCallback);
+ ADD_CALLBACK_FUNCTION(Gendarmes, savegame);
+ ADD_CALLBACK_FUNCTION(Gendarmes, arrestUpdateEntity);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function9);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function10);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function12);
+ ADD_CALLBACK_FUNCTION(Gendarmes, function13);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter2);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter3);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter4);
+ ADD_CALLBACK_FUNCTION(Gendarmes, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Gendarmes, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Gendarmes, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Gendarmes, arrestDraw)
+ arrest(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Gendarmes, arrestPlaysound)
+ arrest(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Gendarmes, arrestPlaysound16)
+ arrest(savepoint, true, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Gendarmes, arrestCallback, uint32)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Gendarmes, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Gendarmes, arrestUpdateEntity, CarIndex, EntityPosition)
+ arrest(savepoint, true, SoundManager::kFlagInvalid, false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IISS(9, Gendarmes, function9, CarIndex, EntityPosition)
+ EntityData::EntityParametersSSS *parameters1 = (EntityData::EntityParametersSSS*)_data->getCurrentParameters(1);
+ EntityData::EntityParametersISII *parameters2 = (EntityData::EntityParametersISII*)_data->getCurrentParameters(2);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (params->param2 <= kPosition_3050) {
+ if (params->param2 != kPosition_3050) {
+ if (params->param2 == kPosition_2740)
+ parameters2->param5 = kObjectCompartment8;
+ } else {
+ parameters2->param5 = kObjectCompartment7;
+ parameters2->param6 = true;
+ }
+ } else if (params->param2 <= kPosition_4840) {
+ if (params->param2 != kPosition_4840) {
+ if (params->param2 == kPosition_4070) {
+ parameters2->param5 = kObjectCompartment6;
+ parameters2->param7 = kPosition_4455;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment5;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_4455;
+ }
+ } else if (params->param2 <= kPosition_6470) {
+ if (params->param2 != kPosition_6470) {
+ if (params->param2 == kPosition_5790) {
+ parameters2->param5 = kObjectCompartment4;
+ parameters2->param7 = kPosition_6130;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment3;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_6130;
+ }
+ } else if (params->param2 != kPosition_7500) {
+ if (params->param2 == kPosition_8200) {
+ parameters2->param5 = kObjectCompartment1;
+ parameters2->param6 = true;
+ parameters2->param7 = kPosition_7850;
+ }
+ } else {
+ parameters2->param5 = kObjectCompartment2;
+ parameters2->param7 = kPosition_7850;
+ }
+
+ if (params->param1 == kCarBaggageRear)
+ parameters2->param5 += 31; // Switch to next compartment car
+
+ if (parameters2->param6) {
+ strcpy((char *)&parameters1->seq1, "632A");
+ strcpy((char *)&parameters1->seq2, "632B");
+ strcpy((char *)&parameters1->seq3, "632C");
+ } else {
+ strcpy((char *)&parameters1->seq1, "632D");
+ strcpy((char *)&parameters1->seq2, "632E");
+ strcpy((char *)&parameters1->seq3, "632F");
+ }
+
+ strcat((char *)&parameters1->seq1, (char *)&params->seq1);
+ strcat((char *)&parameters1->seq2, (char *)&params->seq1);
+ strcat((char *)&parameters1->seq3, (char *)&params->seq1);
+
+ if ((getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)params->param2)
+ || getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)parameters2->param7)
+ || (params->param1 == kCarGreenSleeping && params->param2 == kPosition_8200 && getEntities()->isOutsideAlexeiWindow()))
+ && !getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ setCallback(1);
+ setup_function10((CarIndex)params->param1, (EntityPosition)params->param2, (ObjectIndex)parameters2->param5);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityGendarmes, (char *)&parameters1->seq1);
+ getEntities()->enterCompartment(kEntityGendarmes, (ObjectIndex)CURRENT_PARAM(2, 5));
+
+ setCallback(parameters2->param6 ? 2 : 3);
+ setup_arrestPlaysound(parameters2->param6 ? "POL1044A" : "POL1044B");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ CALLBACK_ACTION();
+ break;
+
+ case 2:
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityGendarmes, (char *)&parameters1->seq2);
+ if (getEntities()->isNobodyInCompartment((CarIndex)params->param1, (EntityPosition)params->param2) || !strcmp(params->seq2, "NODIALOG")) {
+ setCallback(4);
+ setup_arrestCallback(150);
+ } else {
+ char *arrestSound = (char *)&parameters2->seq;
+ strcpy(arrestSound, "POL1045");
+ strcat(arrestSound, (char *)&params->seq2);
+
+ setCallback(5);
+ setup_arrestPlaysound(arrestSound);
+ }
+ break;
+
+ case 4:
+ case 5:
+ if (!getEntities()->isNobodyInCompartment((CarIndex)params->param1, (EntityPosition)params->param2) && strcmp(params->seq2, "NODIALOG")) {
+ char *arrestSound = (char *)&parameters2->seq;
+ strcpy(arrestSound, "POL1043");
+ strcat(arrestSound, (char *)&params->seq2);
+
+ getSound()->playSound(kEntityGendarmes, arrestSound, SoundManager::kFlagInvalid, 30);
+ }
+
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_arrestDraw((char *)&parameters1->seq3);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->exitCompartment(kEntityGendarmes, (ObjectIndex)parameters2->param5);
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_III(10, Gendarmes, function10, CarIndex, EntityPosition, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param5 || getState()->timeTicks > (uint32)params->param5) {
+ if (!params->param5)
+ params->param5 = getState()->timeTicks + 75;
+
+ if (!getEntities()->isOutsideAlexeiWindow() && getObjects()->get((ObjectIndex)params->param3).location != kObjectLocation1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+ }
+ }
+
+ if (!params->param6)
+ params->param6 = getState()->timeTicks + 150;
+
+ if (params->param6 == 0 || getState()->timeTicks > (uint32)params->param6) {
+ params->param6 = kTimeInvalid;
+
+ getSound()->playSound(kEntityGendarmes, "POL1046A", SoundManager::kFlagDefault);
+ }
+
+ UPDATE_PARAM(params->param7, getState()->timeTicks, 300);
+
+ if (!params->param4 && getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ } else {
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityGendarmes, "LIB017", SoundManager::kFlagDefault);
+
+ setCallback(getProgress().jacket == kJacketBlood ? 3 : 4);
+ setup_savegame(kSavegameTypeEvent, getProgress().jacket == kJacketBlood ? kEventMertensBloodJacket : kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionKnock:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_arrestPlaysound16("POL1046B");
+ break;
+
+ case kActionOpenDoor:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ break;
+
+ case kActionDefault:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_arrestPlaysound16("POL1046");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorTalk, kCursorNormal);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation((params->param1 < kCarRedSleeping) ? kEventMertensBloodJacket : kEventCoudertBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+
+ getObjects()->update((ObjectIndex)params->param3, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getObjects()->update((ObjectIndex)params->param3, kEntityGendarmes, getObjects()->get((ObjectIndex)params->param3).location, kCursorNormal, kCursorHand);
+ break;
+
+ case 6:
+ getSound()->playSound(kEntityGendarmes, "LIB014", SoundManager::kFlagDefault);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Gendarmes, chapter1Handler)
+ if (savepoint.action == kAction169499649) {
+ getSavePoints()->push(kEntityGendarmes, kEntityMertens, kAction190082817);
+ setup_function12();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Gendarmes, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getProgress().field_14 = 29;
+
+ setCallback(1);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_5540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function9(kCarGreenSleeping, kPosition_5790, "d", "A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_6220);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function9(kCarGreenSleeping, kPosition_6470, "c", "B");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7250);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function9(kCarGreenSleeping, kPosition_7500, "b", "C");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_7950);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function9(kCarGreenSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_arrestUpdateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping) {
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+
+ setCallback(10);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_2490);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function9(kCarRedSleeping, kPosition_2740, "h", "NODIALOG");
+ break;
+
+ case 11:
+ setCallback(12);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_3820);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_function9(kCarRedSleeping, kPosition_4070, "f", "E");
+ break;
+
+ case 13:
+ setCallback(14);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_4590);
+ break;
+
+ case 14:
+ setCallback(15);
+ setup_function9(kCarRedSleeping, kPosition_4840, "e", "F");
+ break;
+
+ case 15:
+ setCallback(16);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_5540);
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_function9(kCarRedSleeping, kPosition_5790, "d", "G");
+ break;
+
+ case 17:
+ setCallback(18);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_6220);
+ break;
+
+ case 18:
+ setCallback(19);
+ setup_function9(kCarRedSleeping, kPosition_6470, "c", "H");
+ break;
+
+ case 19:
+ setCallback(20);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7250);
+ break;
+
+ case 20:
+ setCallback(21);
+ setup_function9(kCarRedSleeping, kPosition_7500, "b", "J");
+ break;
+
+ case 21:
+ setCallback(22);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_7950);
+ break;
+
+ case 22:
+ setCallback(23);
+ setup_function9(kCarRedSleeping, kPosition_8200, "a", "NODIALOG");
+ break;
+
+ case 23:
+ setCallback(24);
+ setup_arrestUpdateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 24:
+ getProgress().field_14 = 0;
+ getEntities()->clearSequences(kEntityGendarmes);
+ getSavePoints()->push(kEntityGendarmes, kEntityVerges, kAction168710784);
+ setup_function13();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Gendarmes, function13)
+ if (savepoint.action == kActionDefault)
+ getData()->car = kCarNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Gendarmes, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Gendarmes, chapter3)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Gendarmes, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Gendarmes, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityGendarmes);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Gendarmes::arrest(const SavePoint &savepoint, bool shouldPlaySound, SoundManager::FlagType flag, bool checkCallback, bool shouldUpdateEntity) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ TIME_CHECK_CALLBACK_ACTION(params->param1, params->param2);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ // Fallback to next action
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(0, 1) && getEntities()->hasValidFrame(kEntityGendarmes)) {
+ getSound()->playSound(kEntityPlayer, "MUS007");
+ ENTITY_PARAM(0, 1) = 1;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 1000) && !getEntityData(kEntityPlayer)->location) {
+
+ if (shouldUpdateEntity)
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 22) && !getEntities()->isDistanceBetweenEntities(kEntityGendarmes, kEntityPlayer, 250))
+ break;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ // Only handle when passing SIIS params
+ if (!checkCallback) {
+ EXPOSE_PARAMS(EntityData::EntityParametersSIIS);
+
+ if (!shouldPlaySound)
+ getEntities()->drawSequenceRight(kEntityGendarmes, (char *)&params->seq1);
+ else
+ getSound()->playSound(kEntityGendarmes, (char *)&params->seq1, flag);
+ }
+
+ if (shouldUpdateEntity) {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+ if (getEntities()->updateEntity(kEntityGendarmes, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/gendarmes.h b/engines/lastexpress/entities/gendarmes.h
new file mode 100644
index 0000000000..095a74fa44
--- /dev/null
+++ b/engines/lastexpress/entities/gendarmes.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 LASTEXPRESS_GENDARMES_H
+#define LASTEXPRESS_GENDARMES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+#include "lastexpress/game/sound.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Gendarmes : public Entity {
+public:
+ Gendarmes(LastExpressEngine *engine);
+ ~Gendarmes() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_1(arrestDraw, const char *sequence)
+ DECLARE_FUNCTION_1(arrestPlaysound, const char *soundName)
+ DECLARE_FUNCTION_1(arrestPlaysound16, const char *soundName)
+ DECLARE_FUNCTION_1(arrestCallback, uint32 timeValue)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(arrestUpdateEntity, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_4(function9, CarIndex car, EntityPosition entityPosition, const char *sequence1, const char *sequence2)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, ObjectIndex object)
+ DECLARE_FUNCTION(chapter1Handler)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+private:
+ void arrest(const SavePoint &savepoint, bool playSound = false, SoundManager::FlagType flag = SoundManager::kFlagInvalid, bool checkCallback = false, bool shouldUpdateEntity = false);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GENDARMES_H
diff --git a/engines/lastexpress/entities/hadija.cpp b/engines/lastexpress/entities/hadija.cpp
new file mode 100644
index 0000000000..5590c1b6fe
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.cpp
@@ -0,0 +1,532 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/hadija.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Hadija::Hadija(LastExpressEngine *engine) : Entity(engine, kEntityHadija) {
+ ADD_CALLBACK_FUNCTION(Hadija, reset);
+ ADD_CALLBACK_FUNCTION(Hadija, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Hadija, playSound);
+ ADD_CALLBACK_FUNCTION(Hadija, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Hadija, updateEntity);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment6to8);
+ ADD_CALLBACK_FUNCTION(Hadija, compartment8to6);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function12);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function19);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5);
+ ADD_CALLBACK_FUNCTION(Hadija, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Hadija, function22);
+ ADD_CALLBACK_FUNCTION(Hadija, function23);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Hadija, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Hadija, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Hadija, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Hadija, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Hadija, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Hadija, compartment6)
+ COMPARTMENT_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Cf", "619Df");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Hadija, compartment8)
+ COMPARTMENT_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Ch", "619Dh");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Hadija, compartment6to8)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment6, kPosition_4070, "619Bf", kObjectCompartment8, kPosition_2740, "619Ah");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Hadija, compartment8to6)
+ COMPARTMENT_FROM_TO(Hadija, kObjectCompartment8, kPosition_2740, "619Bh", kObjectCompartment6, kPosition_4070, "619Af");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Hadija, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Hadija, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTimeParisEpernay, params->param1, 1, "Har1100", kPosition_4840);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime1084500, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ if (params->param3 != kTimeInvalid && getState()->time > kTime1093500) {
+
+ if (getState()->time <= kTime1134000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param3) {
+ params->param3 = (uint)getState()->time + 75;
+
+ if (!params->param3) {
+ setCallback(3);
+ setup_compartment8();
+ return;
+ }
+ }
+
+ if (params->param3 >= getState()->time)
+ return;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ setCallback(3);
+ setup_compartment8();
+ }
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime1156500, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime1165500) {
+ if (getState()->time <= kTime1188000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !getEntities()->isInsideCompartment(kEntityMahmud, kCarGreenSleeping, kPosition_5790) || !params->param5) {
+ params->param5 = (uint)getState()->time + 75;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_compartment6();
+ return;
+ }
+ }
+
+ if (params->param5 >= getState()->time)
+ return;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ setCallback(5);
+ setup_compartment6();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Hadija, function12)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Hadija, chapter2)
+ if (savepoint.action == kActionDefault) {
+
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Hadija, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1782000 && !params->param1) {
+ params->param1 = 1;
+ getData()->entityPosition = kPosition_2740;
+ }
+
+ if (params->param2 == kTimeInvalid || getState()->time <= kTime1786500) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+
+ if (getState()->time <= kTime1818000) {
+
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time + 75;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_compartment8();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime1822500, params->param3, 2, setup_compartment8to6);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("Har2012");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Hadija, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Hadija, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1998000, params->param1, 1, setup_compartment6to8);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2020500, params->param2, 2, setup_compartment8to6);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2079000, params->param3, 3, setup_compartment6to8);
+
+label_callback3:
+ TIME_CHECK_CALLBACK(kTime2187000, params->param4, 4, setup_compartment8to6);
+
+label_callback4:
+ if (params->param5 != kTimeInvalid && getState()->time > kTime2196000)
+ TIME_CHECK_CAR(kTime2254500, params->param5, 5, setup_compartment6);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityAlouan, kEntityTrain, kAction191070912, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Hadija, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Hadija, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid)
+ TIME_CHECK_CAR(kTime1714500, params->param1, 1, setup_compartment6);
+
+label_callback1:
+ TIME_CHECK_CALLBACK(kTime2367000, params->param2, 2, setup_compartment6to8);
+
+label_callback2:
+ TIME_CHECK_CALLBACK(kTime2421000, params->param3, 3, setup_compartment8to6);
+
+label_callback3:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime2425500)
+ TIME_CHECK_CAR(kTime2484000, params->param4, 4, setup_compartment6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Hadija, function19)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment8, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityHadija);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Hadija, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Hadija, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function22();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Hadija, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function23();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Hadija, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("619AF", kObjectCompartment6);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityHadija);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartment5, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(24, Hadija)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/hadija.h b/engines/lastexpress/entities/hadija.h
new file mode 100644
index 0000000000..bd37a205d9
--- /dev/null
+++ b/engines/lastexpress/entities/hadija.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 LASTEXPRESS_HADIJA_H
+#define LASTEXPRESS_HADIJA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Hadija : public Entity {
+public:
+ Hadija(LastExpressEngine *engine);
+ ~Hadija() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(compartment6)
+ DECLARE_FUNCTION(compartment8)
+ DECLARE_FUNCTION(compartment6to8)
+ DECLARE_FUNCTION(compartment8to6)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_HADIJA_H
diff --git a/engines/lastexpress/entities/ivo.cpp b/engines/lastexpress/entities/ivo.cpp
new file mode 100644
index 0000000000..6bee62f003
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.cpp
@@ -0,0 +1,829 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/ivo.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Ivo::Ivo(LastExpressEngine *engine) : Entity(engine, kEntityIvo) {
+ ADD_CALLBACK_FUNCTION(Ivo, reset);
+ ADD_CALLBACK_FUNCTION(Ivo, draw);
+ ADD_CALLBACK_FUNCTION(Ivo, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Ivo, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Ivo, updateEntity);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Ivo, playSound);
+ ADD_CALLBACK_FUNCTION(Ivo, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Ivo, savegame);
+ ADD_CALLBACK_FUNCTION(Ivo, function11);
+ ADD_CALLBACK_FUNCTION(Ivo, sitAtTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, leaveTableWithSalko);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function16);
+ ADD_CALLBACK_FUNCTION(Ivo, function17);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter2);
+ ADD_CALLBACK_FUNCTION(Ivo, function19);
+ ADD_CALLBACK_FUNCTION(Ivo, function20);
+ ADD_CALLBACK_FUNCTION(Ivo, function21);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, function26);
+ ADD_CALLBACK_FUNCTION(Ivo, function27);
+ ADD_CALLBACK_FUNCTION(Ivo, function28);
+ ADD_CALLBACK_FUNCTION(Ivo, function29);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5);
+ ADD_CALLBACK_FUNCTION(Ivo, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Ivo, fight);
+ ADD_CALLBACK_FUNCTION(Ivo, function33);
+ ADD_CALLBACK_FUNCTION(Ivo, function34);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Ivo, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Ivo, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Ivo, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Ivo, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Ivo, updateFromTicks, uint32)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Ivo, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Ivo, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(8, Ivo, playSound)
+ Entity::playSound(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Ivo, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Ivo, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Ivo, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "809DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityIvo);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ if (getEntities()->isDistanceBetweenEntities(kEntityIvo, kEntitySalko, 750) || getEntities()->checkDistanceFromPosition(kEntitySalko, kPosition_2740, 500)) {
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction123668192);
+
+ setCallback(3);
+ setup_enterExitCompartment("613Ah", kObjectCompartmentH);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Hh");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ }
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Ivo, sitAtTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->clearSequences(kEntitySalko);
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023A1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023A2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023A3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Ivo, leaveTableWithSalko)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityIvo, "023D1");
+ getEntities()->drawSequenceRight(kEntitySalko, "023D2");
+ getEntities()->drawSequenceRight(kEntityTables2, "023D3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Ivo, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Ivo, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntityMilos, kAction135024800);
+ setup_function16();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_updateFromTicks(75);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Ivo, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+
+ setCallback(2);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Ivo, function17)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Ivo, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1777500, params->param1, setup_function19);
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject47, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Ivo, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityIvo, kEntitySalko, kAction136184016);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("809US");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_sitAtTableWithSalko();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ setup_function20();
+ break;
+ }
+ break;
+
+ case kAction102675536:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Ivo, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1809000 && params->param1) {
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_leaveTableWithSalko();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction189688608);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction101106391);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityIvo, kEntityServers1, kAction236237423);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityIvo, "023C2");
+
+ setCallback(1);
+ setup_updateFromTime(450);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Ivo, function21)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Ivo, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Ivo, chapter3Handler)
+ if (savepoint.action == kActionDefault)
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Ivo, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Ivo, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2361600 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityIvo, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityIvo, "023B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Ivo, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_leaveTableWithSalko();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function11();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Ivo, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityIvo);
+ setup_function28();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityIvo, "613Ch");
+ getEntities()->enterCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction88652208);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityIvo, kObjectCompartmentH, true);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityIvo);
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("613FH", kObjectCompartmentH);
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_enterExitCompartment("613Bh", kObjectCompartmentH);
+ break;
+
+ case kAction123852928:
+ setCallback(4);
+ setup_enterExitCompartment("613Dh", kObjectCompartmentH);
+ break;
+
+ case kAction221683008:
+ getSavePoints()->push(kEntityIvo, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Ivo, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2425500 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("613EH", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function29();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Ivo, function29)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityIvo);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Ivo, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityIvo);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Ivo, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_fight();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Ivo, fight)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+ getData()->entityPosition = kPosition_540;
+ getData()->car = kCarBaggageRear;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathIvoFight);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "LIB090");
+ getAction()->playAnimation(kEventCathIvoFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightIvo);
+ if (params->param1) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, true);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 96);
+ setup_function33();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Ivo, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ getObjects()->update(kObject94, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+
+ case kAction135800432:
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Ivo, function34)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityIvo);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/ivo.h b/engines/lastexpress/entities/ivo.h
new file mode 100644
index 0000000000..e726c95af0
--- /dev/null
+++ b/engines/lastexpress/entities/ivo.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_IVO_H
+#define LASTEXPRESS_IVO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Ivo : public Entity {
+public:
+ Ivo(LastExpressEngine *engine);
+ ~Ivo() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param ticks The number of ticks to add
+ */
+ DECLARE_FUNCTION_1(updateFromTicks, uint32 ticks)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(sitAtTableWithSalko)
+ DECLARE_FUNCTION(leaveTableWithSalko)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(fight)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_IVO_H
diff --git a/engines/lastexpress/entities/kahina.cpp b/engines/lastexpress/entities/kahina.cpp
new file mode 100644
index 0000000000..89c685cfe9
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.cpp
@@ -0,0 +1,1528 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/kahina.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Kahina::Kahina(LastExpressEngine *engine) : Entity(engine, kEntityKahina) {
+ ADD_CALLBACK_FUNCTION(Kahina, reset);
+ ADD_CALLBACK_FUNCTION(Kahina, playSound);
+ ADD_CALLBACK_FUNCTION(Kahina, savegame);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kahina, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kahina, function6);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Kahina, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kahina, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function12);
+ ADD_CALLBACK_FUNCTION(Kahina, function13);
+ ADD_CALLBACK_FUNCTION(Kahina, function14);
+ ADD_CALLBACK_FUNCTION(Kahina, function15);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3);
+ ADD_CALLBACK_FUNCTION(Kahina, function19);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kahina, function21);
+ ADD_CALLBACK_FUNCTION(Kahina, function22);
+ ADD_CALLBACK_FUNCTION(Kahina, function23);
+ ADD_CALLBACK_FUNCTION(Kahina, function24);
+ ADD_CALLBACK_FUNCTION(Kahina, function25);
+ ADD_CALLBACK_FUNCTION(Kahina, function26);
+ ADD_CALLBACK_FUNCTION(Kahina, function27);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter4);
+ ADD_CALLBACK_FUNCTION(Kahina, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kahina, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Kahina, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kahina, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Kahina, updateFromTime, uint32)
+ if (savepoint.action == kAction137503360) {
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ }
+
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kahina, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Kahina, function6, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ params->param2 = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_updateEntity2(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_updateEntity2(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 0;
+
+ setCallback(1);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(5);
+ setup_updateEntity2(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 1) || ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityKahina);
+ break;
+ }
+ break;
+
+ case kAction137503360:
+ ENTITY_PARAM(0, 2) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Kahina, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(_entityIndex, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ } else if (getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+
+ if (getData()->car == kCarGreenSleeping || getData()->car == kCarRedSleeping) {
+ ENTITY_PARAM(0, 1) = 1;
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kAction137503360:
+ ENTITY_PARAM(0, 2) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Kahina, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Kahina, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kahina, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kahina, chapter1Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getProgress().jacket != kJacketOriginal)
+ TIME_CHECK_SAVEPOINT(kTime1107000, params->param1, kEntityKahina, kEntityMertens, kAction238732837);
+
+ if (getProgress().eventMertensKronosInvitation)
+ setup_function12();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kahina, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1485000, params->param2, setup_function13);
+ break;
+
+ case kActionKnock:
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ // Fallback to next action
+
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosGoingToInvitation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosGoingToInvitation);
+ break;
+ }
+
+ if (savepoint.action == kActionOpenDoor)
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosGoingToInvitation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80);
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction171849314);
+ params->param1 = 1;
+ }
+ break;
+
+ case kAction137685712:
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kahina, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 || getState()->time >= kTime1201500 || params->param2 == kTimeInvalid || params->param1 >= getState()->time)
+ break;
+
+ if (getState()->time <= kTime1197000) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param2) {
+ params->param2 = (uint)getState()->time;
+
+ if (!getState()->time)
+ goto label_callback;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+label_callback:
+ setCallback(1);
+ setup_function15();
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = (uint)getState()->time + 1800;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kahina, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityKahina, kObjectCompartmentF);
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityKahina, "616Cf");
+ getEntities()->enterCompartment(kEntityKahina, kObjectCompartmentF);
+ getSavePoints()->push(kEntityKahina, kEntityMax, kAction158007856);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kahina, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param2, 0)
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ UPDATE_PARAM_PROC_END
+ }
+ break;
+
+ case kActionDefault:
+ getProgress().field_14 = 19;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->hasValidFrame(kEntityKahina)) {
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isOutsideAlexeiWindow()
+ || getEntities()->isDistanceBetweenEntities(kEntityKahina, kEntityPlayer, 2000)) {
+ if (getProgress().field_14 == 19)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ } else {
+ setCallback(5);
+ setup_enterExitCompartment("616Aa", kObjectCompartment1);
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(7);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getProgress().field_14 == 19)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 8:
+ getEntities()->clearSequences(kEntityKahina);
+ params->param1 = (uint)getState()->time + 4500;
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function14();
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_updateEntity(kCarRedSleeping, kPosition_6470);
+ break;
+
+ case 11:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_6130)) {
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(12);
+ setup_enterExitCompartment("616Ac", kObjectCompartmentC);
+ }
+ break;
+
+ case 12:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, getObjects()->get(kObjectCompartmentC).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject50, kEntityPlayer, getObjects()->get(kObject50).location, kCursorNormal, kCursorNormal);
+
+ setCallback(13);
+ setup_updateFromTime(900);
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentC, kEntityPlayer, getObjects()->get(kObjectCompartmentC).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject50, kEntityPlayer, getObjects()->get(kObject50).location, kCursorHandKnock, kCursorHand);
+
+ setCallback(14);
+ setup_enterExitCompartment("616Bc", kObjectCompartmentC);
+ break;
+
+ case 14:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(15);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 15:
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(16);
+ setup_updateFromTime(900);
+ break;
+
+ case 16:
+ setCallback(17);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 17:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Kahina, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kahina, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 9000)
+ params->param1 = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird) && getEvent(kEventKronosConversationFirebird) && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 900)
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_3:
+ if (getState()->time > kTime1845000 && getEvent(kEventKronosConversationFirebird) && getEntities()->isInKronosSalon(kEntityPlayer)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (getEvent(kEventKronosConversationFirebird))
+ break;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(8);
+ setup_playSound("KRO3003");
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 9 : 10);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(getCallback() + 1);
+ setup_updateFromTime(900);
+ break;
+
+ case 2:
+ case 5:
+ setCallback(getCallback() + 1);
+ setup_playSound("KRO3005");
+ break;
+
+ case 3:
+ goto label_callback_3;
+
+ case 7:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ if (getCallback() == 8)
+ params->param1 = 0;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kahina, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(19, Kahina, function19, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventAnnaBaggageArgument))
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function22);
+
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1019" : "CAT1019A");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityKahina);
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kahina, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEvent(kEventKronosVisit))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ params->param2 = 1;
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+label_callback_2:
+ if (getEntities()->isInKronosSalon(kEntityPlayer))
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+
+ setup_function21();
+ break;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 9000)
+ params->param1 = 1;
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)
+ && !getEvent(kEventKronosConversationFirebird)
+ && getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ UPDATE_PARAM(params->param4, getState()->time, 900);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getEvent(kEventKronosConversationFirebird)) {
+
+ if (getEvent(kEventKahinaAskSpeakFirebird)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversationFirebird);
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getEvent(kEventTatianaGivePoem) || getEvent(kEventTatianaBreakfastGivePoem)) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ setCallback(9);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaAskSpeakFirebird);
+ break;
+ }
+
+ if (params->param1) {
+ if (savepoint.action == kActionKnock)
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getAction()->playAnimation(kEventKahinaAskSpeak);
+ getScenes()->processScene();
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound("KRO3003");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 11 : 12);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ if (getEvent(kEventKronosConversationFirebird)) {
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ } else {
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, false);
+ goto label_callback_1;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ goto label_callback_2;
+
+ case 3:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("KRO3005");
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKronosConversationFirebird);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getScenes()->loadSceneFromPosition(kCarKronos, 80, 1);
+
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_playSound("KRO3005");
+ break;
+
+ case 9:
+ getAction()->playAnimation(kEventKahinaAskSpeakFirebird);
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityKahina, "KRO3004");
+ break;
+
+ case 10:
+ params->param1 = 0;
+ // Fallback to next case
+
+ case 11:
+ case 12:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKahina, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kahina, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (!params->param3)
+ params->param3 = (uint)getState()->time + 4500;
+
+ if (params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param5, 0)
+ setCallback(2);
+ setup_function23();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+label_callback_2:
+ if (params->param2) {
+
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 4500;
+
+ if (params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param3, (getEntities()->isPlayerPosition(kCarKronos, 80) || getEntities()->isPlayerPosition(kCarKronos, 88)), params->param6, 0)
+ getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina));
+ getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina));
+
+ getEntities()->drawSequenceLeft(kEntityKahina, "202a");
+
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (!getProgress().field_44
+ && getState()->time > kTime2214000) {
+
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+
+ if (location == kObjectLocation3 || location == kObjectLocation7) {
+ setCallback(3);
+ setup_function25();
+ } else if (location == kObjectLocation1 || location == kObjectLocation2) {
+ setCallback(4);
+ setup_function26();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityKahina, "202a");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 2:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback_2;
+ }
+ break;
+
+ case kAction92186062:
+ if (params->param1) {
+ setCallback(1);
+ setup_function23();
+ }
+ break;
+
+ case kAction134611040:
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ setup_function24();
+ break;
+
+ case kAction137503360:
+ setup_function22();
+ break;
+
+ case kAction237555748:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kahina, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+
+ if (ENTITY_PARAM(0, 3) || location == kObjectLocation3 || location == kObjectLocation7) {
+ setCallback(1);
+ setup_function25();
+ } else if (location == kObjectLocation2 || location == kObjectLocation1) {
+ setCallback(2);
+ setup_function26();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionDrawScene:
+ if (getData()->car > kCarGreenSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_2740))
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kahina, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPlayer, "LIB014", getSound()->getSoundFlag(kEntityKahina));
+ getSound()->playSound(kEntityPlayer, "LIB015", getSound()->getSoundFlag(kEntityKahina), 15);
+
+ getEntities()->clearSequences(kEntityKahina);
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_4455) || getEntities()->isOutsideAnnaWindow()) {
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+ } else {
+ setCallback(2);
+ setup_enterExitCompartment("616Cf", kObjectCompartmentF);
+ }
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_updateFromTime(900);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, getObjects()->get(kObjectCompartmentF).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, getObjects()->get(kObject53).location, kCursorHandKnock, kCursorHand);
+
+ setCallback(4);
+ setup_enterExitCompartment("616Df", kObjectCompartmentF);
+ break;
+
+ case 4:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityKahina);
+
+ setCallback(6);
+ setup_updateFromTime(900);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 7:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kahina, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && getEntities()->updateEntity(kEntityKahina, (CarIndex)params->param2, (EntityPosition)params->param3)) {
+ getEntities()->clearSequences(kEntityKahina);
+ params->param1 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos))
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kActionOpenDoor);
+ else
+ setup_function27();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function6(kTime2241000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ } else if (ENTITY_PARAM(0, 1)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaGunYellow);
+ } else {
+ setup_function27();
+ }
+ break;
+
+ case 2:
+ if (getEntityData(kEntityPlayer)->entityPosition >= getData()->entityPosition)
+ getAction()->playAnimation(getData()->car < kCarRedSleeping ? kEventKahinaGunYellow : kEventKahinaGunBlue);
+ else
+ getAction()->playAnimation(kEventKahinaGun);
+
+ getEntities()->updateEntity(kEntityKahina, kCarKronos, kPosition_9270);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction235599361);
+ getSound()->playSound(kEntityKahina, "MUS016", SoundManager::kFlagDefault);
+ getProgress().field_44 = 1;
+
+ params->param1 = true;
+ params->param2 = kCarKronos;
+ params->param3 = kPosition_9270;
+ break;
+ }
+ break;
+
+ case kAction137503360:
+ getEntities()->clearSequences(kEntityKahina);
+ if (getSound()->isBuffered(kEntityKahina))
+ getSound()->processEntry(kEntityKahina);
+
+ getProgress().field_44 = 0;
+
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kahina, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid)
+ break;
+
+ if (getState()->time <= kTime2263500) {
+ if (!getEntities()->isPlayerInCar(kCarGreenSleeping) || !params->param1)
+ params->param1 = (uint)getState()->time;
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+
+ setCallback(12);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAnnaBaggageArgument)) {
+ setCallback(1);
+ setup_function19(kCarGreenSleeping, kPosition_8200);
+ break;
+ }
+
+ switch (getInventory()->get(kItemFirebird)->location) {
+ default:
+ break;
+
+ case kObjectLocation3:
+ case kObjectLocation7:
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation3)
+ getProgress().field_7C = 1;
+ else
+ getProgress().field_80 = 1;
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ break;
+ }
+
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_78 = 1;
+ ENTITY_PARAM(0, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 4:
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping)) {
+ setCallback(getCallback() + 1);
+ setup_function19(getCallback() == 1 ? kCarGreenSleeping : kCarKronos, getCallback() == 1 ? kPosition_9460 : kPosition_9270);
+ break;
+ } else {
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850) || getEntities()->isOutsideAlexeiWindow()) {
+ setCallback(6);
+ setup_playSound("LIB013");
+ } else {
+ setCallback(8);
+ setup_enterExitCompartment("616Aa", kObjectCompartment1);
+ }
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 5:
+ case 7:
+ case 11:
+ case 13:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function19(kCarKronos, kPosition_9270);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(9);
+ setup_updateFromTime(900);
+ break;
+
+ case 9:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectHandleBathroom, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (getInventory()->get(kItemFirebird)->location) {
+ default:
+ if (ENTITY_PARAM(0, 3))
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ break;
+
+ case kObjectLocation3:
+ case kObjectLocation7:
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation3)
+ getProgress().field_7C = 1;
+ else
+ getProgress().field_80 = 1;
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_C0 = (uint)getState()->time;
+ getProgress().field_78 = 1;
+ break;
+ }
+
+ getProgress().field_78 = 1;
+ ENTITY_PARAM(0, 3) = 0;
+
+ if (getInventory()->get(kItemFirebird)->location != kObjectLocation18) {
+ setCallback(10);
+ setup_enterExitCompartment("616Ba", kObjectCompartment1);
+ }
+ break;
+
+ case 10:
+ case 12:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(getCallback() + 1);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Kahina, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!getEvent(kEventAnnaBaggageArgument)) {
+ setCallback(1);
+ setup_function19(kCarRedSleeping, kPosition_8200);
+ break;
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ getInventory()->setLocationAndProcess(kItemBriefcase, kObjectLocation2);
+ getProgress().field_78 = 1;
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ setCallback(2);
+ setup_function19(kCarRedSleeping, kPosition_9460);
+ } else {
+ setCallback(6);
+ setup_enterExitCompartment("616Aa", kObjectCompartmentA);
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateFromTime(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function19(kCarRedSleeping, kPosition_8200);
+ break;
+
+ case 4:
+ if (getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ setCallback(5);
+ setup_function19(kCarRedSleeping, kPosition_9270);
+ } else {
+ setCallback(6);
+ setup_enterExitCompartment("616Aa", kObjectCompartmentA);
+ }
+ break;
+
+ case 5:
+ case 9:
+ getEntities()->clearSequences(kEntityKahina);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityKahina);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, getObjects()->get(kObjectCompartmentA).location, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, getObjects()->get(kObject48).location, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_updateFromTime(900);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, getObjects()->get(kObjectCompartmentA).location, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityPlayer, getObjects()->get(kObject48).location, kCursorHandKnock, kCursorHand);
+
+ if (getInventory()->get(kItemFirebird)->location == kObjectLocation1 || getInventory()->get(kItemFirebird)->location == kObjectLocation2) {
+ getScenes()->loadSceneFromItemPosition(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getSavePoints()->push(kEntityKahina, kEntityKronos, kAction138085344);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ setCallback(8);
+ setup_enterExitCompartment("616Ba", kObjectCompartmentA);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(9);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Kahina, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchCar;
+ else if (getEntities()->isPlayerInCar(kCarGreenSleeping))
+ params->param1 = kEventKahinaPunchBlue;
+ else if (getEntities()->isPlayerInCar(kCarRedSleeping))
+ params->param1 = kEventKahinaPunchYellow;
+ else if (getEntities()->isInSalon(kEntityPlayer))
+ params->param1 = kEventKahinaPunchSalon;
+ else if (getEntities()->isInRestaurant(kEntityPlayer))
+ params->param1 = kEventKahinaPunchRestaurant;
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ params->param1 = kEventKahinaPunchKitchen;
+ else if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ params->param1 = kEventKahinaPunchBaggageCarEntrance;
+ else if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarBaggage))
+ params->param1 = kEventKahinaPunchBaggageCar;
+
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kSceneGameOverAlarm2);
+ }
+ break;
+
+ case kActionDefault:
+ getState()->timeDelta = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation((EventIndex)params->param1);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Kahina, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Kahina, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKahina);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kahina.h b/engines/lastexpress/entities/kahina.h
new file mode 100644
index 0000000000..4be9d9fb27
--- /dev/null
+++ b/engines/lastexpress/entities/kahina.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_KAHINA_H
+#define LASTEXPRESS_KAHINA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kahina : public Entity {
+public:
+ Kahina(LastExpressEngine *engine);
+ ~Kahina() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ DECLARE_FUNCTION_1(function6, TimeValue timeValue)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Update the entity, handling excuse me events and resetting the entity state after the argument with Anna in the baggage car
+ *
+ * @param car The car index
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function19, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KAHINA_H
diff --git a/engines/lastexpress/entities/kronos.cpp b/engines/lastexpress/entities/kronos.cpp
new file mode 100644
index 0000000000..3335edb2fb
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.cpp
@@ -0,0 +1,892 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/kronos.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tatiana.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+static const struct {
+ uint32 time;
+ const char *sequence;
+} concertData[54] = {
+ {735, "201d"}, {1395, "201a"}, {1965, "201d"}, {2205, "201a"}, {3405, "201d"},
+ {3750, "201a"}, {3975, "201d"}, {4365, "201a"}, {4650, "201d"}, {4770, "201a"},
+ {4995, "201e"}, {5085, "201d"}, {5430, "201a"}, {5685, "201d"}, {5850, "201a"},
+ {7515, "201d"}, {7620, "201a"}, {7785, "201d"}, {7875, "201a"}, {8235, "201d"},
+ {8340, "201a"}, {8745, "201d"}, {8805, "201a"}, {8925, "201d"}, {8985, "201a"},
+ {9765, "201d"}, {9930, "201a"}, {12375, "201e"}, {12450, "201d"}, {12705, "201c"},
+ {13140, "201d"}, {13305, "201a"}, {13380, "201d"}, {13560, "201a"}, {14145, "201d"},
+ {14385, "201a"}, {14445, "201c"}, {14805, "201a"}, {16485, "201d"}, {16560, "201a"},
+ {16755, "201d"}, {16845, "201a"}, {17700, "201d"}, {17865, "201a"}, {18645, "201d"},
+ {18720, "201a"}, {19410, "201e"}, {19500, "201a"}, {22020, "201d"}, {22185, "201a"},
+ {22590, "201d"}, {22785, "201a"}, {23085, "201d"}, {23265, "201a"}
+};
+
+Kronos::Kronos(LastExpressEngine *engine) : Entity(engine, kEntityKronos) {
+ ADD_CALLBACK_FUNCTION(Kronos, reset);
+ ADD_CALLBACK_FUNCTION(Kronos, savegame);
+ ADD_CALLBACK_FUNCTION(Kronos, updateEntity);
+ ADD_CALLBACK_FUNCTION(Kronos, playSound);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Kronos, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function9);
+ ADD_CALLBACK_FUNCTION(Kronos, function10);
+ ADD_CALLBACK_FUNCTION(Kronos, function11);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter2);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Kronos, function15);
+ ADD_CALLBACK_FUNCTION(Kronos, function16);
+ ADD_CALLBACK_FUNCTION(Kronos, function17);
+ ADD_CALLBACK_FUNCTION(Kronos, function18);
+ ADD_CALLBACK_FUNCTION(Kronos, function19);
+ ADD_CALLBACK_FUNCTION(Kronos, function20);
+ ADD_CALLBACK_FUNCTION(Kronos, function21);
+ ADD_CALLBACK_FUNCTION(Kronos, function22);
+ ADD_CALLBACK_FUNCTION(Kronos, function23);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter4);
+ ADD_CALLBACK_FUNCTION(Kronos, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Kronos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Kronos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Kronos, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Kronos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Kronos, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(6, Kronos, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Kronos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Kronos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param2, setup_function11);
+ break;
+
+ case kAction171849314:
+ params->param1 = 1;
+ break;
+
+ case kAction202621266:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Kronos, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosConversation);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosConversation);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction137685712);
+ setup_function10();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Kronos, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime1489500, params->param1, setup_function11);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ getEntities()->clearSequences(kEntityKronos);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Kronos, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ params->param1++;
+ getSound()->playSound(kEntityKronos, (params->param1 & 1) ? "KRO1001" : "KRO1002");
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7000;
+
+ if (!getSound()->isBuffered(kEntityKronos))
+ getSound()->playSound(kEntityKronos, "KRO1001");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Kronos, chapter2)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Kronos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityKronos);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Kronos, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1993500 && !params->param1 && !params->param2 && !params->param3)
+ setup_function15();
+ break;
+
+ case kAction157159392:
+ switch (savepoint.entity2) {
+ case kEntityAnna:
+ params->param1 = 1;
+ break;
+
+ case kEntityTatiana:
+ params->param2 = 1;
+ break;
+
+ case kEntityAbbot:
+ params->param3 = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Kronos, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getEntities()->isInSalon(kEntityBoutarel)) {
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 75)
+ setup_function16();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param3 != kTimeInvalid && getState()->time > kTime2002500) {
+ if (getState()->time <= kTime2052000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || getEntities()->isInSalon(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time + 900;
+
+ if (params->param3 >= getState()->time)
+ break;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setup_function16();
+ } else {
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+
+ setup_function18();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87))
+ params->param1 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 51) && !getEntities()->isInSalon(kEntityBoutarel))
+ setup_function16();
+ else
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 60)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 59)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 83)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 81)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 87);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Kronos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosVisit);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKronosVisit);
+ getSavePoints()->push(kEntityKronos, kEntityAnna, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityTatiana, kAction101169422);
+ getSavePoints()->push(kEntityKronos, kEntityAbbot, kAction101169422);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 60);
+
+ setup_function17();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Kronos, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9270);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Kronos, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2079000 && !params->param2) {
+ getObjects()->updateLocation2(kObjectCompartmentKronos, kObjectLocation3);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param1 = 1;
+ params->param2 = 1;
+ }
+
+ TIME_CHECK(kTime2106000, params->param3, setup_function19)
+ else {
+ if (params->param1 && getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchSuite4);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6000;
+ getData()->car = kCarKronos;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Kronos, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionDrawScene:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventCathJumpDownCeiling, kSceneNone, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventConcertStart);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityKronos);
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ RESET_ENTITY_STATE(kEntityRebecca, Rebecca, setup_function39);
+ RESET_ENTITY_STATE(kEntitySophie, Sophie, setup_chaptersHandler);
+ RESET_ENTITY_STATE(kEntityAugust, August, setup_function50);
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function56);
+ RESET_ENTITY_STATE(kEntityTatiana, Tatiana, setup_function35);
+
+ setup_function20();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Kronos, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ params->param5 = getSound()->getEntryTime(kEntityKronos)* 2;
+
+ if (params->param6 < ARRAYSIZE(concertData) && params->param5 > concertData[params->param6].time) {
+
+ getEntities()->drawSequenceLeft(kEntityKronos, concertData[params->param6].sequence);
+
+ if (scumm_stricmp(concertData[params->param6].sequence, "201e")) {
+
+ if (scumm_stricmp(concertData[params->param6].sequence, "201c")) {
+
+ if (!scumm_stricmp(concertData[params->param6].sequence, "201d")) {
+ if (getEntities()->isPlayerPosition(kCarKronos, 86))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 86);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 85);
+ } else {
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 86);
+ }
+ } else {
+ if (getEntities()->isPlayerPosition(kCarKronos, 85))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionExit(kEntityKronos, kCarKronos, 86);
+ }
+ } else {
+ if (getEntities()->isPlayerPosition(kCarKronos, 85) || getEntities()->isPlayerPosition(kCarKronos, 86))
+ getScenes()->loadSceneFromPosition(kCarKronos, 83);
+
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 85);
+ getEntities()->updatePositionEnter(kEntityKronos, kCarKronos, 86);
+ }
+
+ ++params->param6;
+ }
+
+ getObjects()->update(kObject76, kEntityKronos, kObjectLocationNone, kCursorNormal, getInventory()->hasItem(kItemBriefcase) ? kCursorHand : kCursorNormal);
+
+ if (!params->param7) {
+ params->param7 = (uint)getState()->time + 2700;
+ params->param8 = (uint)getState()->time + 13500;
+ }
+
+ if (CURRENT_PARAM(1, 2) != kTimeInvalid && params->param7 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(params->param8, !params->param1, CURRENT_PARAM(1, 2), 450)
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction237555748);
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!params->param1)
+ params->param2 = params->param3;
+
+ params->param2 -= getState()->timeDelta;
+
+ if (params->param2 < getState()->timeDelta) {
+
+ getSavePoints()->push(kEntityKronos, kEntityKahina, kAction92186062);
+
+ ++params->param4;
+ switch (params->param4) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 1800;
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 3600;
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathFallingAsleep);
+
+ while (getSound()->isBuffered("1919.LNK"))
+ getSound()->updateQueue();
+
+ getAction()->playAnimation(kEventCathWakingUp);
+ getScenes()->processScene();
+ params->param3 = 162000;
+ break;
+ }
+ params->param2 = params->param3;
+ }
+
+ if (params->param5 > 23400 || CURRENT_PARAM(1, 1)) {
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (CURRENT_PARAM(1, 1)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 26);
+
+ setup_function21();
+ break;
+ }
+
+ if (getEntities()->isInKronosSalon(kEntityPlayer)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventConcertEnd);
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)) {
+ getSound()->playSound(kEntityKronos, "Kro3001");
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ CURRENT_PARAM(1, 1) = 1;
+ break;
+ }
+
+ setup_function21();
+ break;
+
+ case kActionOpenDoor:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventConcertLeaveWithBriefcase);
+ break;
+
+ case kActionDefault:
+ getState()->time = kTime2115000;
+ getState()->timeDelta = 3;
+
+ params->param1 = (getEntities()->isPlayerPosition(kCarKronos, 88)
+ || getEntities()->isPlayerPosition(kCarKronos, 84)
+ || getEntities()->isPlayerPosition(kCarKronos, 85)
+ || getEntities()->isPlayerPosition(kCarKronos, 86)
+ || getEntities()->isPlayerPosition(kCarKronos, 83));
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+ else
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getObjects()->update(kObject76, kEntityKronos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_40 = 1;
+ getEntities()->drawSequenceLeft(kEntityKronos, "201a");
+
+ params->param2 = 2700;
+ params->param3 = 2700;
+ break;
+
+ case kActionDrawScene:
+ params->param1 = (getEntities()->isPlayerPosition(kCarKronos, 88)
+ || getEntities()->isPlayerPosition(kCarKronos, 84)
+ || getEntities()->isPlayerPosition(kCarKronos, 85)
+ || getEntities()->isPlayerPosition(kCarKronos, 86)
+ || getEntities()->isPlayerPosition(kCarKronos, 83));
+
+ if (getInventory()->hasItem(kItemFirebird))
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ else
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_6000;
+ getAction()->playAnimation(kEventConcertLeaveWithBriefcase);
+
+ RESET_ENTITY_STATE(kEntityKahina, Kahina, setup_function21);
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventConcertEnd);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 26);
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Kronos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getProgress().field_40 = 0;
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorNormal, kCursorNormal);
+ getSavePoints()->push(kEntityKronos, kEntityRebecca, kAction191668032);
+ if (!getEvent(kEventConcertLeaveWithBriefcase))
+ setup_function22();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+
+ case kAction235599361:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Kronos, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_44) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaPunchBaggageCarEntrance);
+ } else {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered(savepoint.action == kActionKnock ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+
+ if (getEvent(kEventConcertLeaveWithBriefcase))
+ getSavePoints()->call(kEntityKronos, kEntityKahina, kAction137503360);
+
+ if (getInventory()->hasItem(kItemBriefcase)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKronosReturnBriefcase);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird) && getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringEggCeiling);
+ break;
+ }
+
+ if (getEvent(kEventConcertLeaveWithBriefcase)) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventKronosBringNothing);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityKronos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventKronosReturnBriefcase);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->removeItem(kItemScarf);
+
+ setup_function23();
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventKronosBringEggCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+
+ setup_function23();
+ break;
+
+ case 3:
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation5;
+ getAction()->playAnimation(kEventKronosBringEgg);
+ getScenes()->loadSceneFromPosition(kCarKronos, 87);
+ getInventory()->addItem(kItemBriefcase);
+ setup_function23();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventKronosBringNothing);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventKahinaPunchSuite4);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ break;
+ }
+ break;
+
+ case kAction138085344:
+ setup_function23();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Kronos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInKronosSanctum(kEntityPlayer)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventKahinaWrongDoor);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentKronos, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventKahinaWrongDoor);
+
+ if (getInventory()->hasItem(kItemBriefcase))
+ getInventory()->removeItem(kItemBriefcase);
+
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ getScenes()->loadSceneFromPosition(kCarKronos, 81);
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Kronos, chapter4)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Kronos, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityKronos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/kronos.h b/engines/lastexpress/entities/kronos.h
new file mode 100644
index 0000000000..f73c245347
--- /dev/null
+++ b/engines/lastexpress/entities/kronos.h
@@ -0,0 +1,138 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_KRONOS_H
+#define LASTEXPRESS_KRONOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Kronos : public Entity {
+public:
+ Kronos(LastExpressEngine *engine);
+ ~Kronos() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_KRONOS_H
diff --git a/engines/lastexpress/entities/mahmud.cpp b/engines/lastexpress/entities/mahmud.cpp
new file mode 100644
index 0000000000..0b4dc1b138
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.cpp
@@ -0,0 +1,839 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/entities/mahmud.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Mahmud::Mahmud(LastExpressEngine *engine) : Entity(engine, kEntityMahmud) {
+ ADD_CALLBACK_FUNCTION(Mahmud, reset);
+ ADD_CALLBACK_FUNCTION(Mahmud, draw);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mahmud, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSound);
+ ADD_CALLBACK_FUNCTION(Mahmud, playSoundMertens);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Mahmud, savegame);
+ ADD_CALLBACK_FUNCTION(Mahmud, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mahmud, function10);
+ ADD_CALLBACK_FUNCTION(Mahmud, function11);
+ ADD_CALLBACK_FUNCTION(Mahmud, function12);
+ ADD_CALLBACK_FUNCTION(Mahmud, function13);
+ ADD_CALLBACK_FUNCTION(Mahmud, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter1);
+ ADD_CALLBACK_FUNCTION(Mahmud, resetChapter);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter2);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter3);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter4);
+ ADD_CALLBACK_FUNCTION(Mahmud, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mahmud, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Mahmud, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mahmud, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(4, Mahmud, enterExitCompartment2, ObjectIndex, uint32, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param7, getState()->timeTicks, params->param5);
+
+ if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param6, true);
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMahmud, (char *)&params->seq);
+ getEntities()->enterCompartment(kEntityMahmud, (ObjectIndex)params->param4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(5, Mahmud, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Mahmud, playSoundMertens)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityMertens));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Mahmud, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Mahmud, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mahmud, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getInventory()->hasItem(kItemPassengerList))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1025" : "CAT1025Q");
+ else
+ getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mahmud, function10, ObjectIndex, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param6, getState()->time, 13500);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ break;
+
+ case kActionEndSound:
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ EntityPosition position = getEntityData(kEntityPlayer)->entityPosition;
+ if (position < kPosition_1500 || position >= kPosition_5790 || (position > kPosition_4455 && params->param5 != 5)) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(3);
+ setup_enterExitCompartment("614Ed", kObjectCompartment4);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (!getSound()->isBuffered((savepoint.action == kActionKnock) ? "LIB012" : "LIB013", true))
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+
+ params->param5 = savepoint.param.intValue;
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param3++;
+
+ switch(params->param3) {
+ default:
+ params->param4 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMahmud, "MAH1174");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMahmud, "MAH1173B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170E" : "MAH1173A");
+ break;
+ }
+ }
+
+ if (params->param4) {
+ if (getState()->time >= kTimeCityGalanta) {
+ params->param3 = 0;
+ } else {
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, (getProgress().chapter == kChapter1) ? kSceneGameOverPolice1 : kSceneGameOverPolice2, true);
+ }
+ break;
+ }
+
+ getAction()->handleOtherCompartment((ObjectIndex)savepoint.param.intValue, false, false);
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMahmud, params->param2 ? "MAH1170A" : "MAH1173", SoundManager::kFlagInvalid, 45);
+ getProgress().field_C4 = 1;
+
+ setCallback(1);
+ setup_enterExitCompartment2("614Dd", kObjectCompartment4, 30, (ObjectIndex)params->param1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Md");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+ break;
+
+ case 2:
+ case 3:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Mahmud, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ getSound()->playSound(kEntityPlayer, (savepoint.action == kActionKnock ? "LIB012" : "LIB013"));
+
+ if (!getSound()->isBuffered(kEntityMahmud)) {
+ params->param1++;
+
+ getSound()->playSound(kEntityMahmud, (params->param1 == 1 ? "MAH1170E" : (params->param1 == 2 ? "MAH1173B" : "MAH1174")));
+ }
+
+ switch (getScenes()->get(getState()->scene)->position) {
+ default:
+ break;
+
+ case 55:
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ break;
+
+ case 56:
+ getScenes()->loadSceneFromObject(kObjectCompartment6, true);
+ break;
+
+ case 57:
+ getScenes()->loadSceneFromObject(kObjectCompartment7, true);
+ break;
+
+ case 58:
+ getScenes()->loadSceneFromObject(kObjectCompartment8, true);
+ break;
+ }
+ break;
+ }
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction102227384);
+ setCallback(1);
+ setup_enterExitCompartment("614Ad", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Kd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(2);
+ setup_playSound("MAH1170A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSoundMertens("MAH1170B");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_playSound("MAH1170C");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSoundMertens("MAH1170D");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_playSound("MAH1170E");
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSoundMertens("MAH1170F");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("614Ld", kObjectCompartment4);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction156567128);
+ getEntities()->drawSequenceLeft(kEntityMahmud, "614Bd");
+ getEntities()->enterCompartment(kEntityMahmud, kObjectCompartment4, true);
+
+ setCallback(9);
+ setup_playSound("MAH1170G");
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_playSoundMertens("MAH1170H");
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment5, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 11:
+ getEntities()->exitCompartment(kEntityMahmud, kObjectCompartment4, true);
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction123852928:
+ if (getSound()->isBuffered(kEntityMahmud))
+ getSound()->processEntry(kEntityMahmud);
+
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(11);
+ setup_enterExitCompartment("614Cd", kObjectCompartment4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TODO: factorize code between function12 & function13
+IMPLEMENT_FUNCTION(12, Mahmud, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Ff", kObjectCompartment6);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1105");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gf", kObjectCompartment6);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Mahmud, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("614Gd", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("614Fh", kObjectCompartment8);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ setCallback(4);
+ setup_playSound("Har1107");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("614Gh", kObjectCompartment8);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("614Fd", kObjectCompartment4);
+ break;
+
+ case 7:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMahmud);
+
+ CALLBACK_ACTION();
+ break;
+
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Mahmud, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1)) {
+ params->param2 = 1;
+ getSavePoints()->push(kEntityMahmud, kEntityMertens, kAction204379649);
+ ENTITY_PARAM(0, 1) = 0;
+ }
+
+ if (!params->param2 && getProgress().chapter == kChapter1) {
+
+ TIME_CHECK_CALLBACK(kTime1098000, params->param6, 1, setup_function13);
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param5) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ params->param4 = 1;
+ params->param5 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+ }
+
+ params->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (getProgress().jacket == kJacketBlood || getEvent(kEventMahmudWrongDoor) || getEvent(kEventMahmudWrongDoorOriginalJacket) || getEvent(kEventMahmudWrongDoorDay)) {
+ // Check if we have the passenger list
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(6);
+ setup_playSound(rnd(2) == 0 ? "CAT1501" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(7);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 8 : 9);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ } else {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityMahmud);
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param4 || params->param5) {
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ return;
+
+ case 1:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+
+ if (!getSound()->isBuffered("HAR1104") && getState()->time > kTime1167300 && !params->param7) {
+ params->param7 = 1;
+ setCallback(2);
+ setup_function12();
+ break;
+ }
+
+ params->param8 = 0;
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ params->param8 = 0;
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("MAH1175");
+ break;
+
+ case 5: {
+ CursorStyle cursor = kCursorHand;
+ CursorStyle cursor2 = kCursorHandKnock;
+
+ if (getProgress().jacket == kJacketBlood
+ || getEvent(kEventMahmudWrongDoor)
+ || getEvent(kEventMahmudWrongDoorOriginalJacket)
+ || getEvent(kEventMahmudWrongDoorDay)) {
+ cursor = kCursorNormal;
+ cursor2 = kCursorTalk;
+ }
+
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation1, cursor, cursor2);
+ params->param5 = 1;
+ break;
+ }
+
+ case 6:
+ case 7:
+ params->param4 = 1;
+ break;
+
+ case 8:
+ case 9:
+ setCallback(10);
+ setup_savegame(kSavegameTypeEvent, kEventMahmudWrongDoor);
+ return;
+
+ case 10:
+ getAction()->playAnimation((getProgress().jacket == kJacketGreen) ? (isNight() ? kEventMahmudWrongDoor : kEventMahmudWrongDoorDay) : kEventMahmudWrongDoorOriginalJacket);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->processScene();
+
+ params->param4 = 1;
+ break;
+
+ case 11:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+
+ case 12:
+ getObjects()->update(kObjectCompartment4, kEntityMahmud, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param2 = 0;
+ params->param4 = 0;
+ params->param5 = 0;
+ break;
+ }
+ break;
+
+ case kAction225563840:
+ setCallback(12);
+ setup_function11();
+ break;
+
+ case kAction290410610:
+ params->param3 = (params->param3 < 1) ? 1 : 0;
+ setCallback(11);
+ setup_function10((ObjectIndex)savepoint.param.intValue, (bool)params->param3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Mahmud, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMahmud, kAction170483072, 0);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject20, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Mahmud, resetChapter)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getObjects()->update(kObjectCompartment4, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mahmud, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mahmud, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mahmud, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chaptersHandler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMahmud);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mahmud, chapter5)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMahmud);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mahmud.h b/engines/lastexpress/entities/mahmud.h
new file mode 100644
index 0000000000..647d48b8ed
--- /dev/null
+++ b/engines/lastexpress/entities/mahmud.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAHMUD_H
+#define LASTEXPRESS_MAHMUD_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mahmud : public Entity {
+public:
+ Mahmud(LastExpressEngine *engine);
+ ~Mahmud() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param ticks The time ticks
+ * @param object The object for loading the scene
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment2, const char *sequence, ObjectIndex compartment, uint32 ticks, ObjectIndex object)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSoundMertens, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_2(function10, ObjectIndex, bool)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Reset chapter data
+ */
+ DECLARE_FUNCTION(resetChapter)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAHMUD_H
diff --git a/engines/lastexpress/entities/max.cpp b/engines/lastexpress/entities/max.cpp
new file mode 100644
index 0000000000..a846f7b6dd
--- /dev/null
+++ b/engines/lastexpress/entities/max.cpp
@@ -0,0 +1,628 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/max.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Max::Max(LastExpressEngine *engine) : Entity(engine, kEntityMax) {
+ ADD_CALLBACK_FUNCTION(Max, reset);
+ ADD_CALLBACK_FUNCTION(Max, playSound);
+ ADD_CALLBACK_FUNCTION(Max, draw);
+ ADD_CALLBACK_FUNCTION(Max, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Max, savegame);
+ ADD_CALLBACK_FUNCTION(Max, chapter12_handler);
+ ADD_CALLBACK_FUNCTION(Max, function7);
+ ADD_CALLBACK_FUNCTION(Max, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Max, function9);
+ ADD_CALLBACK_FUNCTION(Max, chapter1);
+ ADD_CALLBACK_FUNCTION(Max, chapter2);
+ ADD_CALLBACK_FUNCTION(Max, chapter3);
+ ADD_CALLBACK_FUNCTION(Max, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Max, freeFromCage);
+ ADD_CALLBACK_FUNCTION(Max, function15);
+ ADD_CALLBACK_FUNCTION(Max, chapter4);
+ ADD_CALLBACK_FUNCTION(Max, function17);
+ ADD_CALLBACK_FUNCTION(Max, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Max, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Max, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Max, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Max, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Max, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Max, chapter12_handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Max, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param2 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ setCallback((savepoint.action == kActionKnock) ? 1 : 2);
+ setup_playSound((savepoint.action == kActionKnock) ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 56) || getEntities()->isPlayerPosition(kCarRedSleeping, 78))
+ getSound()->playSound(kEntityMax, "Max1120");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ case 0:
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("Max1122");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentF, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityMax, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction101687594:
+ getEntities()->clearSequences(kEntityMax);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction122358304:
+ case kActionMaxFreeFromCage:
+ getSavePoints()->push(kEntityMax, kEntityMax, kActionMaxFreeFromCage);
+ getObjects()->update(kObjectCompartmentF, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject53, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction158007856:
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Max, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param3, getState()->time, params->param2);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+
+ params->param2 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionOpenDoor:
+ if (params->param1) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxLickHand);
+ break;
+ }
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxLickHand);
+ getScenes()->processScene();
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ params->param2 = 255 * (4 * rnd(20) + 40);
+
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3101");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Max, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->hasValidFrame(kEntityMax) || !params->param2) {
+
+ params->param2 = (uint)getState()->time;
+ if (!params->param2)
+ goto setup_functions;
+ }
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+setup_functions:
+ if (getProgress().chapter == kChapter3)
+ setup_function15();
+
+ if (getProgress().chapter == kChapter4)
+ setup_function17();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw Max outside of cage
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+
+ params->param1 = (uint)(getState()->time + 2700);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Max, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter12_handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Max, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter12_handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Max, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ break;
+ }
+
+ UPDATE_PARAM(params->param3, getState()->time, params->param1);
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ params->param3 = 0;
+ break;
+
+ case kActionDefault:
+ params->param1 = 255 * (4 * rnd(20) + 40);
+
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction71277948:
+ setCallback(1);
+ setup_function7();
+ break;
+
+ case kAction122358304:
+ params->param2 = 1;
+ break;
+
+ case kActionMaxFreeFromCage:
+ setup_freeFromCage();
+ break;
+
+ case kAction158007856:
+ if (params->param2)
+ break;
+
+ if (!getSound()->isBuffered(kEntityMax)) {
+ getSound()->playSound(kEntityMax, "Max1122");
+ params->param1 = 255 * (4 * rnd(20) + 40);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Max, freeFromCage)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Save game after freeing Max from his cage
+ case kActionOpenDoor:
+ if (getEvent(kEventCathMaxCage)) {
+ if (getEvent(kEventCathMaxFree)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxFree);
+ }
+
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathMaxCage);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCageMax, kEntityMax, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max1122");
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Play animation for Max in the cage and after opening it
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->removeFromQueue(kEntityMax);
+
+ getAction()->playAnimation(kEventCathMaxCage);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityMax);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ if (getSound()->isBuffered(kEntityMax))
+ getSound()->processEntry(kEntityMax);
+
+ getSound()->playSound(kEntityPlayer, "LIB026");
+ getAction()->playAnimation(kEventCathMaxFree);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 92);
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ setup_function9();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Max, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM(params->param3, getState()->time, 900);
+
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ if (!getSound()->isBuffered(kEntityMax))
+ getSound()->playSound(kEntityMax, "Max3010");
+
+ setCallback(1);
+ setup_enterExitCompartment("630Bf", kObjectCompartment4);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getEntities()->enterCompartment(kEntityMax, kObjectCompartmentF, true);
+ getSavePoints()->push(kEntityMax, kEntityAnna, kAction156622016);
+ }
+ break;
+
+ case kAction122358304:
+ (savepoint.entity2 == kEntityAnna) ? (params->param1 = 1) : (params->param2 = 1);
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Max, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPosition_8000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Max, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityCoudert)->entityPosition;
+ getData()->car = getEntityData(kEntityCoudert)->car;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4070;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->drawSequenceLeft(kEntityMax, "630Af");
+ getSavePoints()->push(kEntityMax, kEntityCoudert, kAction157026693);
+ break;
+
+ case kAction122358304:
+ params->param1 = 1;
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ getEntities()->drawSequenceLeft(kEntityMax, "BLANK");
+ break;
+
+ case kActionMaxFreeFromCage:
+ getEntities()->exitCompartment(kEntityMax, kObjectCompartmentF, true);
+ setup_chapter4Handler();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Max, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMax);
+
+ getData()->entityPosition = kPositionNone;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarNone;
+
+ getObjects()->update(kObjectCageMax, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/max.h b/engines/lastexpress/entities/max.h
new file mode 100644
index 0000000000..93eb165a0f
--- /dev/null
+++ b/engines/lastexpress/entities/max.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MAX_H
+#define LASTEXPRESS_MAX_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Max : public Entity {
+public:
+ Max(LastExpressEngine *engine);
+ ~Max() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Handle Chapter 1 & 2 events
+ */
+ DECLARE_FUNCTION(chapter12_handler)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(freeFromCage)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MAX_H
diff --git a/engines/lastexpress/entities/mertens.cpp b/engines/lastexpress/entities/mertens.cpp
new file mode 100644
index 0000000000..d204c204f1
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.cpp
@@ -0,0 +1,4113 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/mertens.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define SAVEGAME_BLOOD_JACKET() \
+ if (getProgress().jacket == kJacketBlood \
+ && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000) \
+ && !getEntities()->isInsideCompartments(kEntityPlayer) \
+ && !getEntities()->checkFields10(kEntityPlayer)) { \
+ setCallback(1); \
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket); \
+ }
+
+Mertens::Mertens(LastExpressEngine *engine) : Entity(engine, kEntityMertens) {
+ ADD_CALLBACK_FUNCTION(Mertens, reset);
+ ADD_CALLBACK_FUNCTION(Mertens, bloodJacket);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Mertens, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Mertens, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound);
+ ADD_CALLBACK_FUNCTION(Mertens, playSound16);
+ ADD_CALLBACK_FUNCTION(Mertens, savegame);
+ ADD_CALLBACK_FUNCTION(Mertens, updateEntity);
+ ADD_CALLBACK_FUNCTION(Mertens, function11);
+ ADD_CALLBACK_FUNCTION(Mertens, bonsoir);
+ ADD_CALLBACK_FUNCTION(Mertens, function13);
+ ADD_CALLBACK_FUNCTION(Mertens, function14);
+ ADD_CALLBACK_FUNCTION(Mertens, function15);
+ ADD_CALLBACK_FUNCTION(Mertens, function16);
+ ADD_CALLBACK_FUNCTION(Mertens, function17);
+ ADD_CALLBACK_FUNCTION(Mertens, function18);
+ ADD_CALLBACK_FUNCTION(Mertens, function19);
+ ADD_CALLBACK_FUNCTION(Mertens, function20);
+ ADD_CALLBACK_FUNCTION(Mertens, function21);
+ ADD_CALLBACK_FUNCTION(Mertens, function22);
+ ADD_CALLBACK_FUNCTION(Mertens, function23);
+ ADD_CALLBACK_FUNCTION(Mertens, function24);
+ ADD_CALLBACK_FUNCTION(Mertens, function25);
+ ADD_CALLBACK_FUNCTION(Mertens, function26);
+ ADD_CALLBACK_FUNCTION(Mertens, tylerCompartment);
+ ADD_CALLBACK_FUNCTION(Mertens, function28);
+ ADD_CALLBACK_FUNCTION(Mertens, function29);
+ ADD_CALLBACK_FUNCTION(Mertens, function30);
+ ADD_CALLBACK_FUNCTION(Mertens, function31);
+ ADD_CALLBACK_FUNCTION(Mertens, function32);
+ ADD_CALLBACK_FUNCTION(Mertens, function33);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1);
+ ADD_CALLBACK_FUNCTION(Mertens, function35);
+ ADD_CALLBACK_FUNCTION(Mertens, function36);
+ ADD_CALLBACK_FUNCTION(Mertens, function37);
+ ADD_CALLBACK_FUNCTION(Mertens, function38);
+ ADD_CALLBACK_FUNCTION(Mertens, function39);
+ ADD_CALLBACK_FUNCTION(Mertens, function40);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function42);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter2);
+ ADD_CALLBACK_FUNCTION(Mertens, function44);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter3);
+ ADD_CALLBACK_FUNCTION(Mertens, function46);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter4);
+ ADD_CALLBACK_FUNCTION(Mertens, function48);
+ ADD_CALLBACK_FUNCTION(Mertens, function49);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5);
+ ADD_CALLBACK_FUNCTION(Mertens, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Mertens, function52);
+ ADD_CALLBACK_FUNCTION(Mertens, function53);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Mertens, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Mertens, bloodJacket)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Mertens, enterExitCompartment, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Mertens, enterExitCompartment2, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ return;
+
+ case kAction4:
+ getEntities()->exitCompartment(kEntityMertens, (ObjectIndex)params->param4);
+ CALLBACK_ACTION();
+ return;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ return;
+ }
+
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIII(5, Mertens, enterExitCompartment3, ObjectIndex, EntityPosition, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->exitCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(_entityIndex, (char *)&params->seq);
+ getEntities()->enterCompartment(_entityIndex, (ObjectIndex)params->param4);
+ getData()->entityPosition = (EntityPosition)params->param5;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param5) || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, (EntityPosition)params->param6)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject((ObjectIndex)params->param4);
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Mertens, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Mertens, playSound)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, Mertens, playSound16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1, SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(9, Mertens, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Mertens, updateEntity, CarIndex, EntityPosition)
+
+#define LOADSCENE_FROM_POSITION() \
+ if (getData()->direction != kDirectionUp) { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750)); \
+ } else { \
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition - 750), true); \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 && getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000))
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem | kItemInvalid);
+ else
+ getData()->inventoryItem = (InventoryItem)(getData()->inventoryItem & kItemToggleHigh);
+
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields10(kEntityPlayer)) {
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if ((ENTITY_PARAM(0, 6) || ENTITY_PARAM(0, 7)) && (!getEvent(kEventKronosConversation) && getProgress().jacket == kJacketGreen)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2) && getProgress().jacket == kJacketGreen && !getProgress().eventMetAugust) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaiting);
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 4) && getState()->time < kTime2133000) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosConcertInvitation);
+ break;
+ }
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ getData()->inventoryItem = kItemNone;
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction1:
+ params->param3 = 0;
+ if (getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD)) {
+ if (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown) {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventMertensDontMakeBed);
+ }
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartment);
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->playSound(kEntityMertens, "CON1110B");
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityMertens);
+ break;
+
+ case kActionDefault:
+ if ((!getProgress().eventCorpseFound && !getEvent(kEventMertensAskTylerCompartment) && !getEvent(kEventMertensAskTylerCompartment))
+ || (ENTITY_PARAM(0, 4) && getProgress().jacket == kJacketGreen && !getEvent(kEventMertensDontMakeBed) && !getProgress().eventCorpseThrown))
+ params->param3 = 1;
+
+ if (getEntities()->updateEntity(kEntityMertens, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 2:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensKronosInvitation : kEventMertensKronosInvitationClosedWindows);
+ getProgress().eventMertensKronosInvitation = true;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (params->param1 != 3 || (params->param2 != kPosition_8200 && params->param2 != kPosition_9510)) {
+ LOADSCENE_FROM_POSITION();
+ break;
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMertensAugustWaiting);
+ getProgress().eventMertensAugustWaiting = true;
+
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (params->param1 == 3 && params->param2 == kPosition_8200) {
+ if (getData()->car == kCarGreenSleeping && getEntities()->checkDistanceFromPosition(kEntityMertens, kPosition_2000, 500))
+ getData()->entityPosition = kPosition_2500;
+
+ getEntities()->updateEntity(kEntityMertens, kCarGreenSleeping, kPosition_2000);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + 750));
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensKronosConcertInvitation);
+ ENTITY_PARAM(2, 4) = 0;
+
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 5:
+ getAction()->playAnimation(getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition ? kEventMertensAskTylerCompartmentD : kEventMertensAskTylerCompartment);
+ LOADSCENE_FROM_POSITION();
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventMertensDontMakeBed);
+ LOADSCENE_FROM_POSITION();
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+ break;
+ }
+
+#undef LOADSCENE_FROM_POSITION
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Mertens, function11, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ UPDATE_PARAM(params->param2, getState()->time, params->param1)
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(12, Mertens, bonsoir, EntityIndex)
+ EntityIndex entity = (EntityIndex)params->param1;
+
+ if (savepoint.action == kActionDefault)
+ return;
+
+ if (getSound()->isBuffered(kEntityMertens)) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ if (isNight()) {
+ if (Entities::isFemale(entity)) {
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112" : "CON1112A");
+ } else {
+ if (entity || getProgress().field_18 != 2) {
+ getSound()->playSound(kEntityMertens, "CON1112F");
+ } else {
+ switch (rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityMertens, "CON1061");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "CON1110G");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "CON1110H");
+ break;
+ }
+ }
+ }
+ } else {
+ if (Entities::isFemale(entity))
+ getSound()->playSound(kEntityMertens, rnd(2) ? "CON1112B" : "CON1112C");
+ else
+ getSound()->playSound(kEntityMertens, "CON1112G");
+ }
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Mertens, function13, bool, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+
+ if (!params->param2 && !params->param3) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 75)
+ getData()->inventoryItem = kItemNone;
+ setCallback(5);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 225)
+ getData()->inventoryItem = kItemNone;
+ setCallback(6);
+ setup_function18();
+ break;
+ UPDATE_PARAM_PROC_END
+
+ getData()->inventoryItem = (getProgress().chapter == kChapter1
+ && !ENTITY_PARAM(2, 1)
+ && !getProgress().eventCorpseFound
+ && !getEvent(kEventMertensAskTylerCompartment)
+ && !getEvent(kEventMertensAskTylerCompartmentD)) ? kItemMatchBox : kItemNone;
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ params->param3++;
+ setCallback(11);
+ setup_bonsoir(savepoint.entity2);
+ break;
+
+ case kActionDefault:
+ if (params->param2)
+ params->param3 = 1;
+
+ if (!getSound()->isBuffered(kEntityMertens)) {
+
+ }
+
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case kAction16:
+ params->param3--;
+
+ if (params->param2 && !params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_function20();
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntityMertens, params->param1 ? "601I" : "601H");
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ case 6:
+ case 9:
+ case 10:
+ CALLBACK_ACTION();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getScenes()->processScene();
+
+ if (!params->param3) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function18();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Mertens, function14, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ SAVEGAME_BLOOD_JACKET();
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ } else {
+ setCallback(1);
+ setup_function11(15);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+
+ setCallback(2);
+ setup_function20();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction202558662);
+ getSavePoints()->push(kEntityMertens, (EntityIndex)params->param1, kAction155853632);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction125499160:
+ if (params->param1 == kEntityVerges)
+ ENTITY_PARAM(0, 8) = 0;
+
+ setCallback(5);
+ setup_function18();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(15, Mertens, function15, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, params->param1 ? "CON1059A" : "CON1059");
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Xb", kObjectCompartment2);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction135664192);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Mertens, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ switch (rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityMertens, "AUG2095A");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "AUG2096A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, "AUG2094B");
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, "AUG2094C");
+ break;
+ }
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, params->param1 ? "AUG2097" : "AUG2098");
+
+ setCallback(4);
+ setup_enterExitCompartment("601Xc", kObjectCompartment3);
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction69239528);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Mertens, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // FIXME: Check that we are using the correct parameter struct
+ if (ENTITY_PARAM(0, 6) || ((EntityData::EntityParametersIIII*)_data->getParameters(8, 1))->hasNonNullParameter()) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601K");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ // Mertens sits on his chair at the back of the train
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ // Got the passenger list, Mertens is looking for it before sitting
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 68)) {
+ getSound()->playSound(kEntityPlayer, "CON1110");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+ }
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMertens);
+ ENTITY_PARAM(2, 1) = 1;
+ setCallback(2);
+ setup_function11(75);
+ break;
+
+ case 2:
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ getSavePoints()->push(kEntityMertens, kEntityMertens, kActionDrawScene);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Mertens, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1)
+ || ENTITY_PARAM(1, 2)
+ || ENTITY_PARAM(1, 3)
+ || ENTITY_PARAM(1, 4)
+ || ENTITY_PARAM(1, 5)
+ || ENTITY_PARAM(1, 6)
+ || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(1, 8)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(2, 1) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!getInventory()->hasItem(kItemPassengerList) || ENTITY_PARAM(0, 2)) {
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ } else {
+ ENTITY_PARAM(0, 2) = 1;
+ getSound()->playSound(kEntityMertens, "CON1058", SoundManager::kFlagInvalid, 75);
+ getEntities()->drawSequenceRight(kEntityMertens, "601D");
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ setCallback(1);
+ setup_callbackActionOnDirection();
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (!ENTITY_PARAM(0, 3)
+ && !getInventory()->hasItem(kItemPassengerList)
+ && ENTITY_PARAM(0, 2)) {
+ getSavePoints()->push(kEntityMertens, kEntityVerges, kAction158617345);
+ ENTITY_PARAM(0, 3) = 1;
+ }
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Mertens, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(2, 1)) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+ ENTITY_PARAM(2, 1) = 0;
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getInventory()->setLocationAndProcess(kItem7, kObjectLocation1);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Mertens, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem7);
+
+ if (ENTITY_PARAM(2, 1)) {
+ ENTITY_PARAM(2, 1) = 0;
+
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_bloodJacket("601C");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(21, Mertens, function21, ObjectIndex, ObjectIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 4), getState()->time, 300)
+ getSound()->playSound(kEntityPlayer, "ZFX1004", getSound()->getSoundFlag(kEntityMertens));
+ UPDATE_PARAM_PROC_END
+
+ UPDATE_PARAM(CURRENT_PARAM(1, 5), getState()->time, 900);
+
+ // Update objects
+ getObjects()->updateLocation2((ObjectIndex)params->param1, kObjectLocation1);
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, (EntityIndex)params->param4, (ObjectLocation)params->param5, (CursorStyle)params->param6, (CursorStyle)params->param7);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, (EntityIndex)params->param8, (ObjectLocation)CURRENT_PARAM(1, 1), (CursorStyle)CURRENT_PARAM(1, 2), (CursorStyle)CURRENT_PARAM(1, 3));
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ params->param3 = 1;
+ params->param4 = getObjects()->get((ObjectIndex)params->param1).entity;
+ params->param5 = getObjects()->get((ObjectIndex)params->param1).location;
+ params->param6 = getObjects()->get((ObjectIndex)params->param1).cursor;
+ params->param7 = getObjects()->get((ObjectIndex)params->param1).cursor2;
+
+ if (params->param2) {
+ params->param8 = getObjects()->get((ObjectIndex)params->param2).entity;
+ CURRENT_PARAM(1, 1) = getObjects()->get((ObjectIndex)params->param2).location;
+ CURRENT_PARAM(1, 2) = getObjects()->get((ObjectIndex)params->param2).cursor;
+ CURRENT_PARAM(1, 3) = getObjects()->get((ObjectIndex)params->param2).cursor2;
+
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ if (params->param5 != kObjectLocation2)
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(params->param3 ? 3 : 4);
+ setup_playSound(params->param3 ? "Con1017" : "Con1017A");
+ break;
+
+ case 3:
+ case 4:
+ params->param3 = 0;
+ getObjects()->update((ObjectIndex)params->param1, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (params->param2)
+ getObjects()->update((ObjectIndex)params->param2, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Mertens, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Mh", kObjectCompartment8);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nh");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment8);
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction225563840);
+ break;
+
+ case 5:
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1170I");
+
+ setCallback(6);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, "MAH1172", SoundManager::kFlagInvalid, 225);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMahmud, kAction123852928);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction102227384:
+ getEntities()->drawSequenceLeft(kEntityMertens, "671Dh");
+ break;
+
+ case kAction156567128:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment8, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Mertens, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vd", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wd");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment4, true);
+
+ setCallback(3);
+ setup_function11(150);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("601Zd", kObjectCompartment4);
+ break;
+
+ case 4:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment4);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(5);
+ setup_function21(kObjectCompartment4, kObject20);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment("671Ad", kObjectCompartment4);
+ break;
+
+ case 6:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Mertens, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Rc", kObjectCompartment3, kPosition_6470, kPosition_6130);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_6470);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("601Sc", kObjectCompartment3);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3, true);
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment3, kObjectKitchen);
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("601Uc", kObjectCompartment3);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ getSavePoints()->push(kEntityMertens, kEntityAugust, kAction192849856);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Qc");
+ break;
+
+ case kAction102675536:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("601Tc", kObjectCompartment3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Mertens, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM(params->param2, getState()->timeTicks, 75);
+
+ setCallback(3);
+ setup_enterExitCompartment3("601Zb", kObjectCompartment2, kPosition_7500, kPositionNone);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction221617184);
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+ break;
+
+ case 3:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(4);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("671Ab", kObjectCompartment2);
+ break;
+
+ case 5:
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2, true);
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (getProgress().chapter == kChapter1 && ENTITY_PARAM(0, 4))
+ if (getProgress().field_14 != 29)
+ getProgress().field_14 = 3;
+
+ setCallback(7);
+ setup_function21(kObjectCompartment2, kObjectHandleInsideBathroom);
+ break;
+
+ case 7:
+ getSound()->playSound(kEntityMertens, "CON1024A");
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(8);
+ setup_enterExitCompartment("641Ub", kObjectCompartment2);
+ break;
+
+ case 8:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityAlexei, kAction124697504);
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ params->param1 = 1;
+ break;
+
+ case kAction156567128:
+ setCallback(6);
+ setup_enterExitCompartment("641Tb", kObjectCompartment2);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Mertens, function26, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().eventCorpseThrown
+ || !params->param1
+ || getProgress().chapter != kChapter1
+ || getProgress().jacket != kJacketGreen) {
+
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound16("ZNU1001");
+ } else {
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound16("CON1062");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ } else if (getProgress().eventCorpseMovedFromFloor) {
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment1);
+ getEntities()->drawSequenceRight(kEntityMertens, "601Ra");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 16);
+
+ setCallback(6);
+ setup_callbackActionOnDirection();
+ } else {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment1);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(7);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ break;
+
+ case 7:
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 1000)) {
+ if (!getEntities()->checkFields10(kEntityPlayer))
+ getSound()->playSound(kEntityMertens, "CON1061");
+ }
+
+ setCallback(9);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(27, Mertens, tylerCompartment, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ UPDATE_PARAM_PROC(params->param2, getState()->timeTicks, 150)
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(10);
+ setup_playSound16("CON1018A");
+ break;
+ UPDATE_PARAM_PROC_END
+
+label_callback10:
+ if (!params->param3)
+ params->param3 = getState()->timeTicks + 300;
+
+ if (params->param3 >= getState()->timeTicks) {
+label_callback11:
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 375);
+
+ getSound()->playSound(kEntityPlayer, "LIB033");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(18);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(20);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(26);
+ setup_function26(false);
+ }
+
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(17);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ } else {
+ params->param3 = kTimeInvalid;
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(11);
+ setup_playSound16("CON1018B");
+ break;
+ }
+
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(13);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(16);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(14);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(12);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ switch (params->param1) {
+ default:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 1:
+ setCallback(23);
+ setup_playSound16("CON1018D");
+ break;
+
+ case 2:
+ setCallback(24);
+ setup_playSound16("CON1018E");
+ break;
+
+ case 3:
+ setCallback(25);
+ setup_playSound16("CON1025");
+ break;
+ }
+
+ } else {
+ setCallback(22);
+ setup_function26(true);
+ }
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB012" : "LIB014");
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+
+ if (getProgress().jacket == kJacketBlood) {
+ setCallback(27);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ switch (params->param1) {
+ case 1:
+ setCallback(29);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAugustWaitingCompartment);
+ break;
+
+ case 2:
+ setCallback(30);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitationCompartment);
+ break;
+
+ case 3:
+ getAction()->playAnimation(isNight() ? kEventMertensPushCallNight : kEventMertensPushCall);
+ // fallback to default case
+
+ default:
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ setCallback(28);
+ setup_function26(false);
+ }
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(26);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ setCallback(params->param1 ? 9 : 8);
+ setup_playSound16(params->param1 ? "CON1018" : "CON1060");
+ } else {
+ getSound()->playSound(kEntityMertens, "CON1019");
+
+ setCallback(1);
+ setup_enterExitCompartment("601Ma", kObjectCompartment1);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(4);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_enterExitCompartment("601Ra", kObjectCompartment1);
+ } else {
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ }
+ }
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseFloor);
+ break;
+
+ case 3:
+ case 12:
+ case 17:
+ case 26:
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseFound ? kSceneGameOverStopPolice : kSceneGameOverPolice, true);
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMertens);
+
+ if (params->param1) {
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+ }
+
+ if (getProgress().eventCorpseThrown || getProgress().chapter != kChapter1) {
+ setCallback(6);
+ setup_function21(kObjectCompartment1, kObjectHandleBathroom);
+ } else {
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping))
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventMertensCorpseBed);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_enterExitCompartment("601Sa", kObjectCompartment1);
+ break;
+
+ case 7:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 8:
+ case 9:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ break;
+
+ case 10:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback10;
+
+ case 11:
+ getObjects()->update(kObjectCompartment1, kEntityMertens, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+ goto label_callback11;
+
+ case 13:
+ case 18:
+ case 27:
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ break;
+
+ case 14:
+ case 19:
+ case 22:
+ case 28:
+ CALLBACK_ACTION();
+ break;
+
+ case 15:
+ case 20:
+ case 29:
+ getAction()->playAnimation(kEventMertensAugustWaitingCompartment);
+ getProgress().eventMertensAugustWaiting = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 16:
+ case 21:
+ case 30:
+ getAction()->playAnimation(kEventMertensKronosInvitationCompartment);
+ getProgress().eventMertensKronosInvitation = true;
+
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 23:
+ getProgress().eventMertensAugustWaiting = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 24:
+ getProgress().eventMertensKronosInvitation = true;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 25:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(28, Mertens, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param4 = 1;
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param5 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SS(29, Mertens, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param7 > 1 && params->param8) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param7++;
+ if (params->param7 == 1)
+ getSound()->playSound(kEntityMertens, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(30, Mertens, function30, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ switch (params->param1) {
+ default:
+ CALLBACK_ACTION();
+ return;
+
+ case 1:
+ params->param2 = kPosition_8200;
+
+ if (getProgress().field_14) {
+ CALLBACK_ACTION();
+ return;
+ }
+
+ getProgress().field_14 = 3;
+ break;
+
+ case 2:
+ params->param2 = kPosition_7500;
+ break;
+
+ case 3:
+ params->param2 = kPosition_6470;
+ break;
+ }
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, (EntityPosition)params->param2);
+ break;
+
+ case 2:
+ switch (params->param1) {
+ default:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 1:
+ if (getProgress().chapter == kChapter4)
+ getSavePoints()->push(kEntityMertens, kEntityTatiana, kAction238790488);
+
+ setCallback(3);
+ setup_tylerCompartment(kMertensAction3);
+ break;
+
+ case 2:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7500)) {
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(4);
+ setup_enterExitCompartment("601Vb", kObjectCompartment2);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_6470)) {
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorNormal, kCursorNormal);
+ params->param3 = 1;
+ }
+
+ setCallback(6);
+ setup_enterExitCompartment("601Mc", kObjectCompartment3);
+ break;
+ }
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Wb");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment2, true);
+
+ setCallback(5);
+ setup_playSound("CON3020");
+ break;
+
+ case 5:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment2, kEntityPlayer, getObjects()->get(kObjectCompartment2).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment2);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601Nc");
+ getEntities()->enterCompartment(kEntityMertens, kObjectCompartment3, true);
+
+ setCallback(7);
+ setup_playSound("CON3020");
+ break;
+
+ case 7:
+ if (params->param3)
+ getObjects()->update(kObjectCompartment3, kEntityPlayer, getObjects()->get(kObjectCompartment3).location, kCursorHandKnock, kCursorHand);
+
+ getEntities()->exitCompartment(kEntityMertens, kObjectCompartment3);
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(31, Mertens, function31, MertensActionType)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setCallback(3);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_bloodJacket("601G");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered(kEntityMertens)) {
+ getEntities()->drawSequenceLeft(kEntityMertens, "601J");
+ } else {
+ setCallback(2);
+ setup_function17();
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Mertens, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9510);
+ break;
+
+ case 2:
+ if (getData()->entityPosition >= kPosition_9460) {
+ getEntities()->clearSequences(kEntityMertens);
+ setCallback(3);
+ setup_function11(900);
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Mertens, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 6)
+ || ENTITY_PARAM(1, 1) || ENTITY_PARAM(1, 2) || ENTITY_PARAM(1, 3) || ENTITY_PARAM(1, 4) || ENTITY_PARAM(1, 5) || ENTITY_PARAM(1, 6) || ENTITY_PARAM(1, 7)
+ || ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(1, 8) = 1;
+
+ setCallback(ENTITY_PARAM(0, 8) ? 1 : 3);
+ setup_updateEntity(kCarGreenSleeping, ENTITY_PARAM(0, 8) ? kPosition_1500 : kPosition_540);
+ } else {
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ ENTITY_PARAM(2, 1) = 1;
+
+ setCallback(2);
+ setup_function14(kEntityVerges);
+ break;
+
+ case 2:
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityMertens);
+
+ setCallback(4);
+ setup_function11(75);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(5);
+ setup_function16(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 5:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(6);
+ setup_function16(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 6:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(7);
+ setup_function15(true);
+ break;
+ }
+ // Fallback to next case
+
+ case 7:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(8);
+ setup_function15(false);
+ break;
+ }
+ // Fallback to next case
+
+ case 8:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(9);
+ setup_function35();
+ break;
+ }
+ // Fallback to next case
+
+ case 9:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(10);
+ setup_function36();
+ break;
+ }
+ // Fallback to next case
+
+ case 10:
+ if (ENTITY_PARAM(1, 3)) {
+ setCallback(11);
+ setup_function40();
+ break;
+ }
+ // Fallback to next case
+
+ case 11:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(12);
+ setup_function28("CON1200");
+ break;
+ }
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 12:
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction168254872);
+ ENTITY_PARAM(1, 1) = 0;
+
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(13);
+ setup_function37();
+ break;
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case 13:
+ ENTITY_PARAM(2, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Mertens, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMertens, kAction171394341, 7);
+ getSavePoints()->addData(kEntityMertens, kAction169633856, 9);
+ getSavePoints()->addData(kEntityMertens, kAction238732837, 10);
+ getSavePoints()->addData(kEntityMertens, kAction269624833, 12);
+ getSavePoints()->addData(kEntityMertens, kAction302614416, 11);
+ getSavePoints()->addData(kEntityMertens, kAction190082817, 8);
+ getSavePoints()->addData(kEntityMertens, kAction269436673, 13);
+ getSavePoints()->addData(kEntityMertens, kAction303343617, 14);
+ getSavePoints()->addData(kEntityMertens, kAction224122407, 17);
+ getSavePoints()->addData(kEntityMertens, kAction201431954, 18);
+ getSavePoints()->addData(kEntityMertens, kAction188635520, 19);
+ getSavePoints()->addData(kEntityMertens, kAction204379649, 4);
+
+ ENTITY_PARAM(0, 1) = 0;
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Mertens, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ break;
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (!ENTITY_PARAM(1, 2) || getProgress().eventMetAugust) {
+ ENTITY_PARAM(1, 2) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ } else {
+ setCallback(5);
+ setup_tylerCompartment(kMertensAction1);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (getProgress().eventMertensAugustWaiting)
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ ENTITY_PARAM(1, 2) = 0;
+
+ setCallback(7);
+ setup_function17();
+ break;
+
+ case 7:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Mertens, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ getProgress().field_14 = 3;
+
+ setCallback(1);
+ setup_function19();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ if (ENTITY_PARAM(0, 6)) {
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_9460);
+ } else {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ }
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 4:
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(7);
+ setup_tylerCompartment(kMertensAction2);
+ } else {
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(5);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function17();
+ break;
+
+ case 7:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ if (!getProgress().eventMertensKronosInvitation)
+ ENTITY_PARAM(0, 7) = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+
+ setCallback(8);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function17();
+ break;
+
+ case 6:
+ case 9:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Mertens, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 >= 2 && params->param2) {
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction125499160);
+
+ setCallback(3);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ }
+ break;
+
+ case kActionEndSound:
+ ++params->param6;
+
+ if (params->param6 == 1)
+ getSound()->playSound(kEntityMertens, getEntities()->isDistanceBetweenEntities(kEntityMertens, kEntityPlayer, 2000) ? "CON1152" : "CON1151");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_1500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601O");
+ getSavePoints()->push(kEntityMertens, kEntityCoudert, kAction154005632);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function17();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction155853632:
+ params->param2 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601L");
+ getSound()->playSound(kEntityMertens, "CON1150");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Mertens, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getProgress().field_14 == 29) {
+ CALLBACK_ACTION();
+ } else {
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(0, 4)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ setCallback(2);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 2:
+ ENTITY_PARAM(0, 4) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Mertens, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 4) = 1;
+
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function22();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function33();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function24();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function33();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function25();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function33();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function38();
+ break;
+
+ case 8:
+ if (getProgress().field_14 == 3)
+ getProgress().field_14 = 0;
+
+ setCallback(9);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_function17();
+ break;
+
+ case 10:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Mertens, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(1, 3) = 0;
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11(1800);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_1500);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function17();
+ break;
+
+ case 5:
+ ENTITY_PARAM(0, 6) = 1;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Mertens, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17();
+ break;
+
+ case 2:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Mertens, function42)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ ENTITY_PARAM(0, 1) = 1;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 1) = 0; // BUG: is set twice. Maybe a bug?
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+
+ params->param1 = 1;
+ params->param2 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ }
+
+ if (ENTITY_PARAM(2, 1) || getProgress().eventCorpseFound || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAskTylerCompartment))
+ getData()->inventoryItem = kItemNone;
+ else
+ getData()->inventoryItem = kItemInvalid;
+
+ if (!params->param2) {
+ TIME_CHECK_SAVEPOINT(kTime1125000, params->param3, kEntityMertens, kEntityMahmud, kAction170483072);
+
+ if (params->param4 != kTimeInvalid && getState()->time > kTimeCityChalons) {
+
+ if (getState()->time <= kTime1188000) {
+ if ((!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getSound()->isBuffered("REB1205")
+ || !getEntities()->isInsideCompartment(kEntityMmeBoutarel, kCarRedSleeping, kPosition_5790)
+ || !params->param4) {
+ params->param4 = (uint)getState()->time;
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ ENTITY_PARAM(0, 4) = kTimeInvalid;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(8);
+ setup_function29("CON1210", "CON1210A");
+ break;
+ }
+ }
+
+label_callback_8:
+ if (getState()->time > kTime1215000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 2700)
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+ ENTITY_PARAM(0, 1) = 1;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (ENTITY_PARAM(0, 8)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(9);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+ if (getProgress().field_14 == 29)
+ goto label_callback_13;
+
+label_callback_9:
+ if (ENTITY_PARAM(1, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(10);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_10:
+ if (ENTITY_PARAM(1, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(11);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_11:
+ if (ENTITY_PARAM(1, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(12);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_12:
+ if (ENTITY_PARAM(1, 4)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(13);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_13:
+ if (ENTITY_PARAM(1, 2)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(14);
+ setup_function35();
+ break;
+ }
+
+label_callback_14:
+ if (ENTITY_PARAM(0, 6)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(15);
+ setup_function36();
+ break;
+ }
+
+label_callback_15:
+ if (ENTITY_PARAM(1, 3)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(16);
+ setup_function40();
+ break;
+ }
+
+label_callback_16:
+ if (ENTITY_PARAM(1, 1)) {
+ ENTITY_PARAM(1, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(17);
+ setup_function28("CON1200");
+ break;
+ }
+
+label_callback_17:
+ if (ENTITY_PARAM(2, 2)) {
+ ENTITY_PARAM(2, 2) = 0;
+ getData()->inventoryItem = kItemNone;
+ setCallback(18);
+ setup_function37();
+ break;
+ }
+
+label_callback_18:
+ if (!params->param1 && ENTITY_PARAM(0, 5)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(19);
+ setup_function39();
+ break;
+ }
+
+label_callback_19:
+ if (ENTITY_PARAM(0, 1) && !getSound()->isBuffered(kEntityMertens)) {
+ if (getProgress().field_18 != 4)
+ getSound()->playSound(kEntityMertens, "CON1505");
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setCallback(21);
+ setup_savegame(kSavegameTypeEvent, kEventMertensAskTylerCompartmentD);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(20);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && ENTITY_PARAM(0, 7) && !getEvent(kEventKronosConversation)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensKronosInvitation);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23) && !getProgress().eventMertensKronosInvitation && !getEvent(kEventMertensLastCar) && !getEvent(kEventMertensLastCarOriginalJacket)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMertensLastCar);
+ break;
+ }
+
+label_callback_2_4:
+ if ((getEntities()->isPlayerPosition(kCarGreenSleeping, 1) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(getEntities()->isPlayerPosition(kCarGreenSleeping, 1) ? 5 : 6);
+ setup_function13(getEntities()->isPlayerPosition(kCarGreenSleeping, 1), false);
+ break;
+ }
+
+label_callback_5_6:
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) && getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (getProgress().jacket == kJacketOriginal || ENTITY_PARAM(0, 7)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(7);
+ setup_function32();
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(kEventMertensKronosInvitation);
+ getProgress().eventMertensKronosInvitation = true;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ goto label_callback_2_4;
+
+ case 3:
+ getAction()->playAnimation(getProgress().jacket == kJacketOriginal ? kEventMertensLastCarOriginalJacket : kEventMertensLastCar);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 6);
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ ENTITY_PARAM(0, 1) = 0;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ case 6:
+ goto label_callback_5_6;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+
+ case 11:
+ goto label_callback_11;
+
+ case 12:
+ goto label_callback_12;
+
+ case 13:
+ goto label_callback_13;
+
+ case 14:
+ goto label_callback_14;
+
+ case 15:
+ goto label_callback_15;
+
+ case 16:
+ goto label_callback_16;
+
+ case 17:
+ goto label_callback_17;
+
+ case 18:
+ goto label_callback_18;
+
+ case 19:
+ params->param1 = 1;
+ goto label_callback_19;
+
+ case 21:
+ getAction()->playAnimation(kEventMertensAskTylerCompartmentD);
+ getEntities()->drawSequenceRight(kEntityMertens, "601A");
+ getInventory()->get(kItem7)->location = kObjectLocationNone;
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 25);
+
+ setCallback(22);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 22:
+ getEntities()->drawSequenceLeft(kEntityMertens, "601B");
+ break;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(23);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ getData()->inventoryItem = kItemNone;
+ setCallback(24);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Mertens, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 0;
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 5) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function44();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Mertens, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(5);
+ setup_function13((bool)savepoint.param.intValue, (bool)savepoint.entity2);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (ENTITY_PARAM(2, 1))
+ break;
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(6);
+ setup_function13(true, false);
+
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(7);
+ setup_function13(false, false);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(8);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(10);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Mertens, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function46();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Mertens, function46)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(5);
+ setup_function14(kEntityVerges);
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(2, 4)
+ && (getEvent(kEventKronosVisit) || getState()->time > kTime2052000)
+ && getState()->time < kTime2133000
+ && getEntities()->isPlayerInCar(kCarGreenSleeping)) {
+ setCallback(6);
+ setup_function32();
+ break;
+ }
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 7, setup_function28, "CON3012");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2117700, params->param2, 8, setup_function32);
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2124000, params->param3, 9, setup_function28, "CON2010");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2146500, params->param4, 10, setup_function32);
+
+label_callback_10:
+ TIME_CHECK_CALLBACK(kTime2169000, params->param5, 11, setup_function32);
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1)) {
+ setCallback(12);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(13);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(14);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+
+ case 10:
+ goto label_callback_10;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(16);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction225932896:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1))
+ getSavePoints()->push(kEntityMertens, kEntityFrancois, kAction205346192);
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(15);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(17);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Mertens, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function17();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function48();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Mertens, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(2, 3)) {
+ params->param1 = 1;
+
+ getObjects()->updateLocation2(kObjectCompartment2, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment3, kObjectLocation1);
+ getObjects()->updateLocation2(kObjectCompartment4, kObjectLocation1);
+
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(2, 3) = 0;
+ }
+
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(1);
+ setup_function16(true);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(2);
+ setup_function16(false);
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(3);
+ setup_function15(true);
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(4);
+ setup_function15(false);
+ break;
+ }
+
+label_callback_4:
+ if (!params->param1) {
+ TIME_CHECK_CALLBACK(kTime2403000, params->param2, 5, setup_function49);
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2430000, params->param3, 6, setup_function32);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK(kTime2439000, params->param4, 7, setup_function32);
+
+label_callback_7:
+ TIME_CHECK_CALLBACK(kTime2448000, params->param5, 8, setup_function32);
+ }
+
+label_callback_8:
+ if (getState()->time > kTime2538000 && !ENTITY_PARAM(0, 1) && !ENTITY_PARAM(2, 1)) {
+ UPDATE_PARAM(params->param6, getState()->time, 2700);
+
+ getEntities()->drawSequenceLeft(kEntityMertens, "601E");
+
+ ENTITY_PARAM(0, 1) = 1;
+ params->param6 = 0;
+ }
+ break;
+
+ case kAction11:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(9);
+ setup_function13((bool)savepoint.param.intValue, savepoint.entity2 != kEntityPlayer);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_1500;
+ getData()->location = kLocationOutsideCompartment;
+
+ getScenes()->loadSceneFromItemPosition(kItem7);
+ break;
+
+ case kActionDrawScene:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 1)) {
+ setCallback(10);
+ setup_function13(true, false);
+ } else if (getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ setCallback(11);
+ setup_function13(false, false);
+ }
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+
+ case kAction225358684:
+ if (!ENTITY_PARAM(0, 1)) {
+ setCallback(13);
+ setup_function30((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+
+ case kAction226078300:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(12);
+ setup_playSound("CON2020");
+ }
+ break;
+
+ case kAction305159806:
+ if (!ENTITY_PARAM(2, 1) && !ENTITY_PARAM(0, 1)) {
+ setCallback(14);
+ setup_function31((MertensActionType)savepoint.param.intValue);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Mertens, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function19();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_tylerCompartment(kMertensActionNone);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function33();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function25();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function33();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function24();
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function33();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_function23();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function17();
+ break;
+
+ case 11:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Mertens, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMertens);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Mertens, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function52();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Mertens, function52)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("Mme5010");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ params->param1 = (uint)(getState()->time + 4500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("671Ad", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityMertens, kEntityMmeBoutarel, kAction155604840);
+ setup_function53();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Mertens, function53)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(3);
+ setup_playSound(getSound()->justCheckingCath());
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 4 : 5);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_5790);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("601ZD", kObjectCompartment4);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMertens);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ // Fallback to next case
+
+ case 3:
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 4:
+ case 5:
+ params->param3++;
+
+ if (params->param3 == 1) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("Con5002");
+
+ } else if (params->param3 == 2) {
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(7);
+ setup_playSound("Con5002A");
+ }
+ break;
+
+ case 6:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartment4, kEntityMertens, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 7:
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(54, Mertens)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mertens.h b/engines/lastexpress/entities/mertens.h
new file mode 100644
index 0000000000..ccce17795c
--- /dev/null
+++ b/engines/lastexpress/entities/mertens.h
@@ -0,0 +1,220 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_MERTENS_H
+#define LASTEXPRESS_MERTENS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Mertens : public Entity {
+private:
+ // The type of action when entering Tyler compartment
+ enum MertensActionType {
+ kMertensActionNone = 0,
+ kMertensAction1 = 1,
+ kMertensAction2 = 2,
+ kMertensAction3 = 3
+ };
+
+public:
+ Mertens(LastExpressEngine *engine);
+ ~Mertens() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handle meeting Coudert with the blooded jacket
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(bloodJacket, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ * @param entityPosition1 The entity position
+ * @param entityPosition1 The entity position to check
+ *
+ * @note We are not using the shared function due to too many differences
+ */
+ DECLARE_FUNCTION_4(enterExitCompartment3, const char *sequence, ObjectIndex compartment, EntityPosition entityPosition1, EntityPosition entityPosition2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function11, uint32 time)
+
+ /**
+ * Says "Bonsoir" to another character
+ *
+ * @param entity The entity
+ */
+ DECLARE_FUNCTION_1(bonsoir, EntityIndex entity)
+ DECLARE_FUNCTION_2(function13, bool, bool)
+ DECLARE_FUNCTION_1(function14, EntityIndex entity)
+ DECLARE_FUNCTION_1(function15, bool)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+
+ /**
+ * ???
+ *
+ * @param object1 First object index
+ * @param object2 Second object index
+ */
+ DECLARE_FUNCTION_2(function21, ObjectIndex object1, ObjectIndex object2)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, bool)
+ DECLARE_FUNCTION_1(tylerCompartment, MertensActionType action)
+ DECLARE_FUNCTION_1(function28, const char *soundName)
+ DECLARE_FUNCTION_2(function29, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION_1(function30, MertensActionType action)
+ DECLARE_FUNCTION_1(function31, MertensActionType action)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function42)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(function44)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function46)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function52)
+ DECLARE_FUNCTION(function53)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MERTENS_H
diff --git a/engines/lastexpress/entities/milos.cpp b/engines/lastexpress/entities/milos.cpp
new file mode 100644
index 0000000000..50b0c04f45
--- /dev/null
+++ b/engines/lastexpress/entities/milos.cpp
@@ -0,0 +1,1805 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/milos.h"
+
+#include "lastexpress/entities/vesna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Milos::Milos(LastExpressEngine *engine) : Entity(engine, kEntityMilos) {
+ ADD_CALLBACK_FUNCTION(Milos, reset);
+ ADD_CALLBACK_FUNCTION(Milos, draw);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Milos, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Milos, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Milos, playSound);
+ ADD_CALLBACK_FUNCTION(Milos, playSound16);
+ ADD_CALLBACK_FUNCTION(Milos, savegame);
+ ADD_CALLBACK_FUNCTION(Milos, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Milos, enterCompartmentDialog);
+ ADD_CALLBACK_FUNCTION(Milos, function11);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1);
+ ADD_CALLBACK_FUNCTION(Milos, function13);
+ ADD_CALLBACK_FUNCTION(Milos, function14);
+ ADD_CALLBACK_FUNCTION(Milos, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function16);
+ ADD_CALLBACK_FUNCTION(Milos, function17);
+ ADD_CALLBACK_FUNCTION(Milos, function18);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2);
+ ADD_CALLBACK_FUNCTION(Milos, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function21);
+ ADD_CALLBACK_FUNCTION(Milos, chapter3);
+ ADD_CALLBACK_FUNCTION(Milos, function23);
+ ADD_CALLBACK_FUNCTION(Milos, function24);
+ ADD_CALLBACK_FUNCTION(Milos, function25);
+ ADD_CALLBACK_FUNCTION(Milos, function26);
+ ADD_CALLBACK_FUNCTION(Milos, function27);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4);
+ ADD_CALLBACK_FUNCTION(Milos, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function30);
+ ADD_CALLBACK_FUNCTION(Milos, function31);
+ ADD_CALLBACK_FUNCTION(Milos, function32);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5);
+ ADD_CALLBACK_FUNCTION(Milos, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Milos, function35);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Milos, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Milos, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Milos, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(4, Milos, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Milos, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Milos, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(7, Milos, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Milos, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(9, Milos, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Milos, enterCompartmentDialog, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ if (getEvent(kEventMilosTylerCompartmentDefeat)) {
+ // Robert saying: "Milos"
+ switch(rnd(3)) {
+ default:
+ case 0:
+ getSound()->playSound(kEntityPlayer, "CAT1014");
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPlayer, "CAT1014A");
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityPlayer, "CAT1014B");
+ break;
+ }
+ } else {
+ getSound()->excuseMeCath();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(11, Milos, function11, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param5 && params->param1 < getState()->time && !params->param7) {
+ params->param7 = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param8, getState()->timeTicks, 75)
+ params->param2 = 0;
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ UPDATE_PARAM_PROC_END
+ }
+
+ params->param8 = 0;
+
+ if (getProgress().chapter != kChapter1 || params->param5)
+ break;
+
+ if (params->param6) {
+ UPDATE_PARAM_PROC(CURRENT_PARAM(1, 1), getState()->time, 4500)
+ params->param6 = 0;
+ CURRENT_PARAM(1, 1) = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getProgress().field_CC) {
+
+ if (ENTITY_PARAM(0, 3) && !getProgress().field_14 && !params->param6) {
+ getProgress().field_14 = 14;
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction190412928);
+
+ setCallback(1);
+ setup_enterExitCompartment("609Cg", kObjectCompartmentG);
+ }
+ break;
+ }
+
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 18000;
+
+ if (CURRENT_PARAM(1, 2) != kTimeInvalid) {
+ if (params->param4 >= getState()->time) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityMilos, 2000) || !CURRENT_PARAM(1, 2))
+ CURRENT_PARAM(1, 2) = (uint)getState()->time + 150;
+
+ if (CURRENT_PARAM(1, 2) >= getState()->time)
+ break;
+ }
+
+ CURRENT_PARAM(1, 2) = kTimeInvalid;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityPlayer, kEntityMilos, 2000))
+ getProgress().field_98 = 1;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(6);
+ setup_playSound("MIL1012");
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(10);
+ setup_playSound((rnd(2) ? "CAT1504" : getSound()->wrongDoorCath()));
+ } else {
+ setCallback(11);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 7 : 8);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param3 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch(getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_8200);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ if (getProgress().field_14 == 14)
+ getProgress().field_14 = 0;
+
+ params->param6 = 1;
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 6:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 7:
+ case 8:
+ setCallback(9);
+ // Milos asking: "Yeah? Who is it?"
+ setup_playSound("MIL1117A");
+ break;
+
+ case 9:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 13:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param5 = 0;
+ break;
+
+ }
+ break;
+
+ case kAction122865568:
+ getData()->location = kLocationOutsideCompartment;
+ setCallback(12);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ params->param1 = 13;
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction221683008:
+ params->param5 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Milos, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getSavePoints()->addData(kEntityMilos, kAction157691176, 0);
+ getSavePoints()->addData(kEntityMilos, kAction208228224, 2);
+ getSavePoints()->addData(kEntityMilos, kAction259125998, 3);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Milos, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kActionDrawTablesWithChairs, "009E");
+ getEntities()->clearSequences(kEntityVesna);
+ getEntities()->clearSequences(kEntityIvo);
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntitySalko, "009D5");
+ getEntities()->drawSequenceRight(kEntityTables2, "009D4");
+ getEntities()->drawSequenceRight(kEntityIvo, "009D3");
+ getEntities()->drawSequenceRight(kEntityVesna, "009D2");
+ getEntities()->drawSequenceRight(kEntityMilos, "009D1");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Milos, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_14 == 29 || getProgress().field_14 == 3) {
+ if (params->param2) {
+ setCallback(1);
+ setup_enterExitCompartment("609Ca", kObjectCompartment1);
+ } else {
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+
+ if (params->param1) {
+
+ // TODO replace with UPDATE_PARAM_PROC (without the kTimeInvalid part)
+ if (!CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = getState()->timeTicks + 45;
+
+ if (CURRENT_PARAM(1, 1) < getState()->timeTicks) {
+
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 2), getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ ++params->param5;
+ switch (params->param5) {
+ default:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+
+ case 1:
+ setCallback(6);
+ setup_playSound("LIB013");
+ break;
+
+ case 2:
+ setCallback(8);
+ setup_playSound("LIB012");
+ break;
+
+ case 3:
+ setCallback(10);
+ setup_playSound("LIB012");
+ break;
+
+ case 4:
+ ++params->param7;
+
+ if (params->param7 < 3) {
+ params->param5 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ } else {
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBedVisit : kEventMilosTylerCompartmentVisit;
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentVisit);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ }
+ }
+ break;
+ }
+
+ // TODO replace with UPDATE_PARAM_PROC (without the kTimeInvalid part)
+ if (!CURRENT_PARAM(1, 3))
+ CURRENT_PARAM(1, 3) = getState()->timeTicks + 75;
+
+ if (CURRENT_PARAM(1, 3) < getState()->timeTicks) {
+
+ if (!params->param4) {
+ setCallback(12);
+ setup_playSound("MIL1030C");
+ break;
+ }
+
+label_callback_12:
+ UPDATE_PARAM(CURRENT_PARAM(1, 4), getState()->timeTicks, 75);
+
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartment1, true);
+
+ if (getProgress().eventCorpseMovedFromFloor) {
+ setCallback(13);
+ setup_enterExitCompartment("609Ba", kObjectCompartment1);
+ break;
+ }
+
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(14);
+ setup_enterExitCompartment2("609Ba", kObjectCompartment1);
+ break;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarNone, 1);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ break;
+
+ case kActionKnock:
+ if (params->param2) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(20);
+ setup_playSound("LIB012");
+ } else if (!params->param3) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ setCallback(22);
+ setup_playSound16("MIL1032");
+ }
+ break;
+
+ case kActionOpenDoor:
+ if (getProgress().eventCorpseMovedFromFloor && getProgress().jacket != kJacketBlood) {
+ if (params->param2) {
+ getEntityData(kEntityPlayer)->location = kLocationInsideCompartment;
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBed : kEventMilosTylerCompartment;
+ } else {
+ params->param6 = (getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) ? kEventMilosTylerCompartmentBedVisit : kEventMilosTylerCompartmentVisit;
+ }
+
+ setCallback(17);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentVisit);
+ } else {
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(16);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_7850)
+ || getEntities()->isOutsideAlexeiWindow()) {
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isOutsideAlexeiWindow())
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 49);
+
+ getSound()->playSound(kEntityPlayer, "LIB012");
+
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorTalk, kCursorHand);
+
+ params->param1 = 1;
+ } else {
+ getEntities()->drawSequenceLeft(kEntityMilos, "609Aa");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartment1, true);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseMovedFromFloor ? kSceneGameOverBloodJacket : kSceneGameOverPolice1, true);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ getAction()->playAnimation((EventIndex)params->param6);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 4:
+ case 18:
+ params->param8 = getFight()->setup(kFightMilos);
+ if (params->param8) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param8 == Fight::kFightEndLost);
+ } else {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+ getProgress().field_CC = 1;
+
+ setCallback(getCallback() + 1);
+ setup_savegame(kSavegameTypeEvent, kEventMilosTylerCompartmentDefeat);
+ }
+ break;
+
+ case 5:
+ case 19:
+ getAction()->playAnimation(kEventMilosTylerCompartmentDefeat);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+ getData()->location = kLocationOutsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_playSound16("MIL1031C");
+ break;
+
+ case 7:
+ case 9:
+ case 11:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, params->param3 < 1 ? kCursorTalk : kCursorNormal, kCursorHand);
+ CURRENT_PARAM(1, 2) = 0;
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_playSound16("MIL1031A");
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_playSound16("MIL1031B");
+ break;
+
+ case 12:
+ params->param4 = 1;
+ goto label_callback_12;
+
+ case 13:
+ params->param2 = 1;
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 14:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(15);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorpseFloor);
+ break;
+
+ case 15:
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ break;
+
+ case 16:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getAction()->playAnimation(kEventMilosCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, getProgress().eventCorpseMovedFromFloor ? kSceneGameOverBloodJacket : kSceneGameOverPolice1, true);
+ break;
+
+ case 17:
+ getSound()->playSound(kEntityPlayer, getObjects()->get(kObjectCompartment1).location == kObjectLocation1 ? "LIB032" : "LIB014");
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getAction()->playAnimation((EventIndex)params->param6);
+
+ setCallback(18);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 20:
+ setCallback(21);
+ setup_playSound("MIL1117A");
+ break;
+
+ case 21:
+ getObjects()->update(kObjectCompartment1, kEntityMilos, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 22:
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityMilos, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Milos, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1071000, params->param3, kEntityMilos, kEntityServers1, kAction223002560);
+
+ if (getState()->time > kTime1089000 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ setup_function16();
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 61) && !params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->timeTicks, 45)
+ setCallback(1);
+ setup_draw("009C");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 70) && !params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 45);
+
+ setCallback(2);
+ setup_draw("009C");
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityMilos, kEntityTables2, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param1 = 1;
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Milos, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(5);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function13();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityServers1, kAction269485588);
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction125242096);
+ getEntities()->drawSequenceRight(kEntityMilos, "807DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityMilos);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 3:
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityVesna, 750)
+ || getEntities()->checkDistanceFromPosition(kEntityVesna, kPosition_3050, 500)) {
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction123668192);
+
+ setCallback(4);
+ setup_enterExitCompartment("611Ag", kObjectCompartmentG);
+ } else {
+ params->param1 = 1;
+
+ getEntities()->drawSequenceLeft(kEntityMilos, "609Dg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMilos);
+
+ setup_function17();
+ break;
+ }
+ break;
+
+ case kAction135024800:
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction204832737);
+
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Milos, function17)
+ if (savepoint.action == kActionDefault) {
+ setCallback(1);
+ setup_function11(kTimeBedTime);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Milos, function18)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Milos, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject46, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Milos, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Milos, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param2, getState()->time, 4500);
+
+ params->param1 = 1;
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitAugust);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (!getEvent(kEventMilosCompartmentVisitAugust)
+ && !getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)
+ && params->param1)
+ setup_chapter2Handler();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Mil1118");
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventMilosCompartmentVisitAugust);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 5);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction135024800);
+
+ setCallback(4);
+ setup_function11(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Milos, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->events[kEventMilosCompartmentVisitAugust])
+ setup_function24();
+ else
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Milos, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2106000 && !params->param1) {
+ params->param1 = 1;
+
+ setCallback(1);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction137165825);
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerInCar(kCarRedSleeping)
+ && !getEntities()->isPlayerPosition(kCarRedSleeping, 1)) {
+ setCallback(3);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+
+ case 2:
+ case 4:
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction101687594);
+
+ setup_function24();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment("609Bg", kObjectCompartmentG);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Milos, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param4)
+ params->param4 = (uint)getState()->time + 4500;
+
+ if (params->param4 < getState()->time) {
+ params->param4 = kTimeInvalid;
+ params->param3 = 1;
+ }
+
+ if (ENTITY_PARAM(0, 1)) {
+ setCallback(1);
+ setup_enterExitCompartment("609Cg", kObjectCompartmentG);
+ break;
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(9);
+ setup_playSound(rnd(2) ? "CAT1504" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(10);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ setCallback(6);
+ setup_playSound("LIB012");
+ }
+ break;
+
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getState()->time >= kTime2106000) {
+ setCallback(12);
+ setup_playSound("LIB013");
+ } else {
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(11);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitAugust);
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (getEvent(kEventMilosCompartmentVisitAugust)
+ || getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)
+ || !params->param3
+ || getState()->time >= kTime2106000) {
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+ }
+
+ setup_function23();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction203663744);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_function26(kTime2223000);
+ break;
+
+ case 2:
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCorridorThanksD);
+ } else {
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation((getData()->entityPosition < getEntityData(kEntityPlayer)->entityPosition) ? kEventMilosCorridorThanksD : kEventMilosCorridorThanks);
+
+ if (getData()->car == kCarRedSleeping && getEntities()->checkDistanceFromPosition(kEntityMilos, kPosition_3050, 500))
+ getData()->entityPosition = kPosition_3550;
+
+ getEntities()->updateEntity(kEntityMilos, kCarRedSleeping, kPosition_3050);
+ getEntities()->loadSceneFromEntityPosition(getData()->car, (EntityPosition)(getData()->entityPosition + (750 * (getData()->direction == kDirectionDown ? 1 : -1))), getData()->direction != kDirectionDown);
+
+ setCallback(4);
+ setup_enterCompartmentDialog(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("609BG", kObjectCompartmentG);
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityMilos);
+ getData()->location = kLocationInsideCompartment;
+ ENTITY_PARAM(0, 1) = 0;
+
+ setup_function25();
+ break;
+
+ case 6:
+ if (getEvent(kEventMilosCompartmentVisitAugust) || getState()->time >= kTime2106000) {
+ setCallback(8);
+ setup_playSound("Mil1117A");
+ } else {
+ setCallback(7);
+ setup_playSound("Mil1118");
+ }
+ break;
+
+ case 7:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 8:
+ case 13:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 9:
+ case 10:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+
+ case 11:
+ getAction()->playAnimation(kEventMilosCompartmentVisitAugust);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 5);
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction135024800);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_playSound("MIL1117A");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Milos, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4)) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 13500)
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424);
+ params->param3 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param4 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, params->param1 ? kObjectLocation3 : kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1505" : "CAT1505A");
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ if (!getEvent(kEventMilosCompartmentVisitTyler) && !getProgress().field_54 && !ENTITY_PARAM(0, 4))
+ getSavePoints()->push(kEntityMilos, kEntityVesna, kAction155913424);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ if (getEntities()->isInsideCompartment(kEntityVesna, kCarRedSleeping, kPosition_3050)) {
+ setCallback(3);
+ setup_playSound("VES1015A");
+ break;
+ }
+
+ if (getEvent(kEventMilosCompartmentVisitTyler) || ENTITY_PARAM(0, 4)) {
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+
+ RESET_ENTITY_STATE(kEntityVesna, Vesna, setup_chapter3Handler);
+
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventMilosCompartmentVisitTyler);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 1;
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventMilosCompartmentVisitTyler);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 5);
+ getObjects()->update(kObjectCompartmentG, kEntityMilos, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 5:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(26, Milos, function26, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param2) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping)) {
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setCallback(2);
+ setup_function27(kCarGreenSleeping, kPosition_540);
+ } else {
+ setCallback(3);
+ setup_function27(kCarRedSleeping, kPosition_9460);
+ }
+ }
+ break;
+
+ case kActionDefault:
+ ENTITY_PARAM(0, 2) = 0;
+
+ setCallback(1);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+
+ case 2:
+ case 3:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ setCallback(4);
+ setup_updateFromTime(450);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function27(kCarRedSleeping, kPosition_540);
+ break;
+
+ case 5:
+ if (ENTITY_PARAM(0, 2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityMilos);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(27, Milos, function27, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 1000)
+ && !getEntities()->isInGreenCarEntrance(kEntityPlayer)
+ && !getEntities()->isInsideCompartments(kEntityPlayer)
+ && !getEntities()->checkFields10(kEntityPlayer)) {
+ if (getData()->car == kCarRedSleeping || getData()->car == kCarGreenSleeping) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ CALLBACK_ACTION();
+ }
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityMilos, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Milos, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Milos, chapter4Handler)
+#define TIME_CHECK_PLAYSOUND_MILOS(timeValue, parameter, sound) \
+ if (getState()->time > timeValue && !parameter) { \
+ parameter = 1; \
+ getSound()->playSound(kEntityMilos, sound); \
+ if (getEntities()->isDistanceBetweenEntities(kEntityMilos, kEntityPlayer, 2000)) \
+ getProgress().field_94 = 1; \
+ break; \
+ }
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1)
+ break;
+
+ if (params->param2) {
+ setup_function30();
+ break;
+ }
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2356200, params->param3, "Mil4013");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2360700, params->param4, "Mil4014");
+
+ TIME_CHECK_PLAYSOUND_MILOS(kTime2370600, params->param5, "Mil4015");
+
+ TIME_CHECK_SAVEPOINT(kTime2407500, params->param6, kEntityMilos, kEntityVesna, kAction55996766);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityMilos, "611Cg");
+ getEntities()->enterCompartment(kEntityMilos, kObjectCompartmentG, true);
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction88652208);
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityMilos, kObjectCompartmentG);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_3050;
+
+ getEntities()->clearSequences(kEntityMilos);
+
+ params->param1 = 0;
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ setCallback(1);
+ setup_enterExitCompartment("611Bg", kObjectCompartmentG);
+ break;
+
+ case kAction123852928:
+ setCallback(2);
+ setup_enterExitCompartment("611Dg", kObjectCompartmentG);
+ break;
+
+ case kAction135600432:
+ params->param2 = 1;
+ break;
+
+ case kAction221683008:
+ if (getSound()->isBuffered(kEntityMilos))
+ getSound()->processEntry(kEntityMilos);
+
+ params->param1 = 1;
+ getSavePoints()->push(kEntityMilos, kEntityCoudert, kAction123199584);
+ break;
+ }
+
+#undef TIME_CHECK_PLAYSOUND_MILOS
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Milos, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11(kTime2410200);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityMilos, kEntityIvo, kAction55996766);
+
+ setCallback(2);
+ setup_function11(kTime2412000);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityMilos, kEntitySalko, kAction55996766);
+
+ setCallback(3);
+ setup_function11(kTime2415600);
+ break;
+
+ case 3:
+ setup_function31();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Milos, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_enterExitCompartment("609CG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_enterCompartmentDialog(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ setup_function32();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Milos, function32)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMilos);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Milos, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMilos);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarCoalTender;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Milos, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ if (!getProgress().isNightTime) {
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventTrainStopped);
+ break;
+ }
+
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverTrainStopped2, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getAction()->playAnimation(isNight() ? kEventLocomotiveMilosShovelingNight : kEventLocomotiveMilosShovelingDay);
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ if (getSound()->isBuffered("ARRIVE"))
+ getSound()->removeFromQueue("ARRIVE");
+
+ getSound()->processEntries();
+ getAction()->playAnimation(isNight() ? kEventLocomotiveMilosNight : kEventLocomotiveMilosDay);
+ getSound()->setupEntry(SoundManager::kSoundType7, kEntityMilos);
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 1);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventLocomotiveAnnaStopsTrain);
+ getLogic()->gameOver(kSavegameTypeEvent2, kEventLocomotiveMilosDay, kSceneGameOverTrainStopped, true);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventLocomotiveRestartTrain);
+ getAction()->playAnimation(kEventLocomotiveOldBridge);
+ getSound()->resetState();
+ getState()->time = kTime2983500;
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 5:
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 2, 1);
+ getSavePoints()->push(kEntityMilos, kEntityAbbot, kAction135600432);
+
+ setup_function35();
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventTrainStopped);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverTrainStopped, true);
+ break;
+ }
+ break;
+
+ case kAction168646401:
+ if (!getEvent(kEventLocomotiveMilosShovelingDay) && !getEvent(kEventLocomotiveMilosShovelingNight)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveMilosShovelingDay);
+ break;
+ }
+
+ if (!getEvent(kEventLocomotiveMilosDay) && !getEvent(kEventLocomotiveMilosNight)) {
+ if (getProgress().isNightTime && getState()->time < kTimeTrainStopped2)
+ getState()->time = kTimeTrainStopped2;
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveMilosDay);
+ }
+ break;
+
+ case kAction169773228:
+ if (!getProgress().isNightTime) {
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveAnnaStopsTrain);
+ }
+
+ getSound()->processEntry(kEntityMilos);
+ if (getState()->time < kTimeTrainStopped2)
+ getState()->time = kTimeTrainStopped2;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveRestartTrain);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Milos, function35)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityMilos);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/milos.h b/engines/lastexpress/entities/milos.h
new file mode 100644
index 0000000000..6d44d1c4d9
--- /dev/null
+++ b/engines/lastexpress/entities/milos.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_MILOS_H
+#define LASTEXPRESS_MILOS_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Milos : public Entity {
+public:
+ Milos(LastExpressEngine *engine);
+ ~Milos() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(enterCompartmentDialog, CarIndex car, EntityPosition entityPosition)
+ DECLARE_FUNCTION_1(function11, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION_1(function26, TimeValue timeValue)
+ DECLARE_FUNCTION_2(function27, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function35)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MILOS_H
diff --git a/engines/lastexpress/entities/mmeboutarel.cpp b/engines/lastexpress/entities/mmeboutarel.cpp
new file mode 100644
index 0000000000..aeaa1e631e
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.cpp
@@ -0,0 +1,1301 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/mmeboutarel.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+MmeBoutarel::MmeBoutarel(LastExpressEngine *engine) : Entity(engine, kEntityMmeBoutarel) {
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, reset);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, playSound);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, draw);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateFromTime);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, updateEntity);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function8);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function9);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function11);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function13);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function14);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function15);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function16);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function19);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function24);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function25);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(MmeBoutarel, function28);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, MmeBoutarel, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, MmeBoutarel, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, MmeBoutarel, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, MmeBoutarel, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, MmeBoutarel, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, MmeBoutarel, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_5790, kPosition_6130, kCarRedSleeping, kObjectCompartmentD, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, MmeBoutarel, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getInventory()->hasItem(kItemPassengerList) ? getSound()->playSound(kEntityPlayer, "CAT1021") : getSound()->excuseMeCath();
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(8, MmeBoutarel, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4 && params->param5) {
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606U");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction169557824);
+ break;
+
+ case kAction155853632:
+ params->param4 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606L");
+ getSound()->playSound(kEntityMmeBoutarel, (char *)&params->seq1);
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000)) {
+ if (getProgress().chapter == kChapter1)
+ getProgress().field_A8 = 1;
+ else if (getProgress().chapter == kChapter3)
+ getProgress().field_A4 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, MmeBoutarel, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getData()->entityPosition = getEntityData(kEntityBoutarel)->entityPosition;
+ getData()->location = getEntityData(kEntityBoutarel)->location;
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityBoutarel, kAction203520448);
+ break;
+
+ case 3:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 5:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(5);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, MmeBoutarel, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityMmeBoutarel, kAction242526416, 0);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, MmeBoutarel, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid)
+ break;
+
+ if (params->param1 >= getState()->time) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 1000) || !params->param2)
+ params->param2 = (uint)getState()->time + 150;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ setCallback(1);
+ setup_playSound("MME1040");
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 1800;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("MME1040A");
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_playSound("MME1041");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateFromTime(900);
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, MmeBoutarel, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44)) {
+ setCallback(1);
+ setup_draw("502B");
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Qd");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ params->param1 = 1;
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function13();
+ break;
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+
+ case kAction168986720:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction102752636);
+ getSound()->playSound(kEntityMmeBoutarel, "MME1036");
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+
+ setCallback(3);
+ setup_enterExitCompartment("606Fd", kObjectCompartmentD);
+ break;
+
+ case kAction202221040:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationOutsideCompartment;
+
+ getSound()->playSound(kEntityMmeBoutarel, "MME1035A");
+
+ if (getEntities()->hasValidFrame(kEntityMmeBoutarel) || getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000) )
+ getProgress().field_AC = 1;
+
+ setCallback(2);
+ setup_enterExitCompartment("606Ed", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, MmeBoutarel, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getSound()->isBuffered(kEntityMmeBoutarel) && params->param6 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(params->param1, !getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000), params->param6, 0)
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityMmeBoutarel, kEntityPlayer, 2000))
+ getProgress().field_A0 = 1;
+
+ params->param5 = 1;
+
+ setCallback(1);
+ setup_playSound("MME1037");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback_1:
+ if (getProgress().field_24 && params->param7 != kTimeInvalid) {
+ UPDATE_PARAM_PROC_TIME(kTime1093500, (!params->param5 || !getEntities()->isPlayerInCar(kCarRedSleeping)), params->param7, 0)
+ setCallback(2);
+ setup_function11();
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+ TIME_CHECK(kTime1094400, params->param8, setup_function14);
+
+ if (params->param4) {
+ UPDATE_PARAM(CURRENT_PARAM(1, 1), getState()->timeTicks, 75);
+
+ params->param3 = 1;
+ params->param4 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ CURRENT_PARAM(1, 1) = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param4) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(7);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(8);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ ++params->param2;
+
+ setCallback(savepoint.action == kActionKnock ? 4 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = (uint)getState()->time + 900;
+ getData()->entityPosition = kPosition_5790;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param4) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ params->param4 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback_1;
+
+ case 2:
+ setup_function14();
+ break;
+
+ case 3:
+ case 4:
+ setCallback(params->param2 <= 1 ? 6 : 5);
+ setup_playSound(params->param2 <= 1 ? "MME1038" : "MME1038C");
+ break;
+
+ case 5:
+ case 6:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param4 = 1;
+ break;
+
+ case 7:
+ case 8:
+ params->param3 = 1;
+ params->param4 = 0;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, MmeBoutarel, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "503");
+
+ setCallback(3);
+ setup_playSound("MRB1080");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(4);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ setup_function15();
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, MmeBoutarel, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTimeEnterChalons && !params->param4) {
+ params->param4 = 1;
+
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+ }
+
+label_callback_5:
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 1;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(10);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(11);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ break;
+ }
+
+ ++params->param3;
+
+ setCallback(savepoint.action == kActionKnock ? 7 : 6);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->car = kCarRedSleeping;
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 0;
+ params->param3 = 0; // BUG" why param3 when it's always param2?
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function8("MME1101");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("606Td", kObjectCompartmentD);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5790;
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback_5;
+
+ case 6:
+ case 7:
+ if (params->param3 <= 1) {
+ setCallback(9);
+ setup_playSound("MME1038");
+ } else {
+ setCallback(8);
+ setup_playSound("MME1038C");
+ }
+ break;
+
+ case 8:
+ case 9:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 10:
+ case 11:
+ params->param1 = 1;
+ params->param2 = 0;
+ break;
+
+ case 12:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction223068211:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(12);
+ setup_playSound("MME1151B");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, MmeBoutarel, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, MmeBoutarel, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, MmeBoutarel, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isInsideCompartment(kEntityFrancois, kCarRedSleeping, kPosition_5790)) {
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ } else {
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "606Md");
+ getEntities()->enterCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getData()->location = kLocationInsideCompartment;
+ setup_function19();
+ break;
+ }
+ break;
+
+ case kAction100901266:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case kAction100957716:
+ getEntities()->exitCompartment(kEntityMmeBoutarel, kObjectCompartmentD, true);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_enterExitCompartment2("606Ad", kObjectCompartmentD);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, MmeBoutarel, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 44) && !params->param2) {
+ if (params->param1) {
+ setCallback(1);
+ setup_draw("502B");
+ } else {
+ params->param1 = 1;
+ }
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping , 44))
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 11);
+ }
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param2 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param2 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, MmeBoutarel, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, MmeBoutarel, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 1) && params->param2 != kTimeInvalid) {
+
+ if (getState()->time <= kTime2038500) {
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping)
+ || !params->param1
+ || getSound()->isBuffered("FRA2012")
+ || getSound()->isBuffered("FRA2010")
+ ||!params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time)
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction189872836);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject43, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("606Rd", kObjectCompartmentD);
+ break;
+
+ case 2:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function8("MME3001");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_5790);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_enterExitCompartment2("606Td", kObjectCompartmentD);
+ break;
+
+ case 6:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setCallback(7);
+ setup_updateFromTime(150);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("606Dd", kObjectCompartmentD);
+ break;
+
+ case 8:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityFrancois, kAction190390860);
+ break;
+
+ case 9:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(9);
+ setup_function9();
+ break;
+
+ case kAction102484312:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+
+ case kAction134289824:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "502A");
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, MmeBoutarel, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, MmeBoutarel, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param2, getState()->time, 900);
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorKeepValue, kCursorKeepValue);
+
+ setCallback(1);
+ setup_enterExitCompartment("606Cd", kObjectCompartmentD);
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ setup_function24();
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMmeBoutarel, "501");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction101107728:
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, MmeBoutarel, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTime2470500, params->param4, setup_function25);
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param1 = 1;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param2) {
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1510" : getSound()->wrongDoorCath());
+ } else {
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ }
+ } else {
+ ++params->param3;
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(params->param3 > 1 ? 3 : 4);
+ setup_playSound(params->param3 > 1 ? "MME1038C" : "MME1038");
+ break;
+
+ case 3:
+ case 4:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 5:
+ case 6:
+ params->param1 = 1;
+ params->param2 = 0;
+ break;
+
+ case 7:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction123199584);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityMmeBoutarel, kEntityCoudert, kAction88652208);
+ break;
+ }
+ break;
+
+ case kAction122865568:
+ setCallback(8);
+ setup_playSound("Mme1151A");
+ break;
+
+ case kAction221683008:
+ setCallback(7);
+ setup_playSound("Mme1038");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, MmeBoutarel, function25)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentD, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, MmeBoutarel, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, MmeBoutarel, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function28();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, MmeBoutarel, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(1);
+ setup_playSound(getSound()->justCheckingCath());
+ break;
+ }
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 3);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_5790;
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityMmeBoutarel);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound("Mme5001");
+ break;
+
+ case 4:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentD, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject51, kEntityMmeBoutarel, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(29, MmeBoutarel)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/mmeboutarel.h b/engines/lastexpress/entities/mmeboutarel.h
new file mode 100644
index 0000000000..1f1d762dd9
--- /dev/null
+++ b/engines/lastexpress/entities/mmeboutarel.h
@@ -0,0 +1,164 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_MMEBOUTAREL_H
+#define LASTEXPRESS_MMEBOUTAREL_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class MmeBoutarel : public Entity {
+public:
+ MmeBoutarel(LastExpressEngine *engine);
+ ~MmeBoutarel() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function8, const char *soundName)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function28)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MMEBOUTAREL_H
diff --git a/engines/lastexpress/entities/pascale.cpp b/engines/lastexpress/entities/pascale.cpp
new file mode 100644
index 0000000000..2c8c29177b
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.cpp
@@ -0,0 +1,1232 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/pascale.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Pascale::Pascale(LastExpressEngine *engine) : Entity(engine, kEntityPascale) {
+ ADD_CALLBACK_FUNCTION(Pascale, draw);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Pascale, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Pascale, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Pascale, updatePosition);
+ ADD_CALLBACK_FUNCTION(Pascale, playSound);
+ ADD_CALLBACK_FUNCTION(Pascale, draw2);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, sitSophieAndRebecca);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeCath);
+ ADD_CALLBACK_FUNCTION(Pascale, function11);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1);
+ ADD_CALLBACK_FUNCTION(Pascale, getMessageFromAugustToTyler);
+ ADD_CALLBACK_FUNCTION(Pascale, sitAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, serveTatianaVassili);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function18);
+ ADD_CALLBACK_FUNCTION(Pascale, function19);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter2);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function23);
+ ADD_CALLBACK_FUNCTION(Pascale, welcomeAbbot);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function27);
+ ADD_CALLBACK_FUNCTION(Pascale, messageFromAnna);
+ ADD_CALLBACK_FUNCTION(Pascale, function29);
+ ADD_CALLBACK_FUNCTION(Pascale, function30);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5);
+ ADD_CALLBACK_FUNCTION(Pascale, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Pascale, function33);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(1, Pascale, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Pascale, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Pascale, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityPascale);
+ params->param1 = 1;
+ }
+
+ return;
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(4, Pascale, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Pascale, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Pascale, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(7, Pascale, draw2)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Pascale, welcomeSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getSound()->playSound(kEntityPascale, "REB1198", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter3:
+ getSound()->playSound(kEntityPascale, "REB3001", SoundManager::kFlagInvalid, 30);
+ break;
+
+ case kChapter4:
+ getSound()->playSound(kEntityPascale, "REB4001", SoundManager::kFlagInvalid, 30);
+ break;
+ }
+
+ setCallback(2);
+ setup_sitSophieAndRebecca();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityRebecca, kAction157370960);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Pascale, sitSophieAndRebecca)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityPascale, "012C1");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012C2");
+ getEntities()->drawSequenceLeft(kEntityTables3, "012C3");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Pascale, welcomeCath)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 && !getSound()->isBuffered(kEntityPascale))
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 64);
+ break;
+
+ case kActionExitCompartment:
+ if (!params->param2) {
+ params->param2 = 1;
+
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+ }
+
+ CALLBACK_ACTION();
+ break;
+
+ case kAction4:
+ if (!params->param1) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityPascale, "HED1001");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 64);
+ getEntities()->drawSequenceRight(kEntityPascale, "035A");
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 && getEntities()->isPlayerPosition(kCarRestaurant, 64)) {
+ getSound()->playSound(kEntityPascale, "HED1001A");
+ getSound()->playSound(kEntityPlayer, "LIB004");
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 69);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Pascale, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168046720);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168046720);
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(1);
+ setup_welcomeCath();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction168627977);
+ getSavePoints()->push(kEntityPascale, kEntityAlexei, kAction168627977);
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 55);
+
+ setCallback(2);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Pascale, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityPascale, kAction239072064, 0);
+ getSavePoints()->addData(kEntityPascale, kAction257489762, 2);
+ getSavePoints()->addData(kEntityPascale, kAction207769280, 6);
+ getSavePoints()->addData(kEntityPascale, kAction101824388, 7);
+ getSavePoints()->addData(kEntityPascale, kAction136059947, 8);
+ getSavePoints()->addData(kEntityPascale, kAction223262556, 1);
+ getSavePoints()->addData(kEntityPascale, kAction269479296, 3);
+ getSavePoints()->addData(kEntityPascale, kAction352703104, 4);
+ getSavePoints()->addData(kEntityPascale, kAction352768896, 5);
+ getSavePoints()->addData(kEntityPascale, kAction191604416, 10);
+ getSavePoints()->addData(kEntityPascale, kAction190605184, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Pascale, getMessageFromAugustToTyler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!ENTITY_PARAM(1, 3)) {
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E");
+ getEntities()->drawSequenceLeft(kEntityAugust, "BLANK");
+
+ setCallback(2);
+ setup_playSound("AUG1001");
+ break;
+ }
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityPascale, "010B");
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityPascale);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kActionDeliverMessageToTyler);
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Pascale, sitAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExitCompartment:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 62);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceRight(kEntityTables0, "001C3");
+ getEntities()->drawSequenceRight(kEntityAnna, "001C2");
+ getEntities()->drawSequenceRight(kEntityPascale, "001C1");
+
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 62);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Pascale, welcomeAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("901");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityPascale, "ANN1047");
+
+ setCallback(2);
+ setup_sitAnna();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAnna, kAction157370960);
+
+ setCallback(3);
+ setup_draw("904");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Pascale, serveTatianaVassili)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("903");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "014B");
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ if (getSound()->isBuffered("TAT1069A"))
+ getSound()->processEntry("TAT1069A");
+ else if (getSound()->isBuffered("TAT1069B"))
+ getSound()->processEntry("TAT1069B");
+
+ setCallback(2);
+ setup_playSound("TAT1066");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityTatiana, kAction122288808);
+
+ setCallback(3);
+ setup_draw("906");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Pascale, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 5) && ENTITY_PARAM(0, 6)) {
+ setup_function18();
+ break;
+ }
+
+ if (!getEntities()->isSomebodyInsideRestaurantOrSalon())
+ goto label_callback3;
+
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(0, 1) && !ENTITY_PARAM(1, 3)) {
+ setCallback(2);
+ setup_getMessageFromAugustToTyler();
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_serveTatianaVassili();
+ break;
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(4);
+ setup_welcomeAnna();
+ break;
+ }
+
+label_callback4:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(5);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Pascale, function18)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (getState()->time > kTime1242000 && !params->param1) {
+ params->param1 = 1;
+
+ getSavePoints()->push(kEntityPascale, kEntityServers0, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityServers1, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityCooks, kAction101632192);
+ getSavePoints()->push(kEntityPascale, kEntityVerges, kAction101632192);
+
+ setup_function19();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Pascale, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntityData(kEntityPlayer)->entityPosition < kPosition_3650) {
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Pascale, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Pascale, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Pascale, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ goto label_callback;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Pascale, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityPascale, kCarRestaurant, 67);
+
+ setCallback(1);
+ setup_welcomeAbbot();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->updatePositionExit(kEntityPascale, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityPascale, kEntityAbbot, kAction122288808);
+
+ setCallback(2);
+ setup_draw("906");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 7) = 0;
+ getEntities()->clearSequences(kEntityPascale);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Pascale, welcomeAbbot)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ getSound()->playSound(kEntityPascale, "ABB3015A");
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kAction10:
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kAction136455232);
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityPascale, "ABB3015", SoundManager::kFlagInvalid, 105);
+ getEntities()->drawSequenceRight(kEntityPascale, "029A1");
+ getEntities()->drawSequenceRight(kEntityAbbot, "029A2");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Pascale, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Pascale, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2511000 && !params->param4) {
+ params->param2 = 1;
+ params->param4 = 1;
+ }
+
+ if (!getEntities()->isInKitchen(kEntityPascale))
+ break;
+
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ if (ENTITY_PARAM(0, 8)) {
+ setCallback(1);
+ setup_function27();
+ break;
+ }
+
+label_callback1:
+ if (ENTITY_PARAM(1, 2) && ENTITY_PARAM(1, 4)) {
+ if (!params->param3)
+ params->param3 = (uint)(getState()->time + 9000);
+
+ if (params->param5 != kTimeInvalid) {
+
+ if (params->param3 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time;
+
+ if (params->param5 < getState()->time) {
+ params->param5 = kTimeInvalid;
+ setCallback(2);
+ setup_messageFromAnna();
+ break;
+ }
+ }
+ }
+
+label_callback2:
+ if (params->param1 && !params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+ }
+
+label_callback3:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_welcomeSophieAndRebecca();
+ }
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+ break;
+
+ case kActionDrawScene:
+ if (!params->param2) {
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71))
+ params->param2 = 1;
+
+ if (!params->param2 && getEntities()->isPlayerPosition(kCarRestaurant, 61))
+ params->param1 = 1;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ params->param1 = 0;
+ params->param2 = 1;
+ goto label_callback3;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 4) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+
+ getSavePoints()->push(kEntityPascale, kEntityTables0, kActionDrawTablesWithChairs, "001P");
+ getSavePoints()->push(kEntityPascale, kEntityTables1, kActionDrawTablesWithChairs, "005J");
+ getSavePoints()->push(kEntityPascale, kEntityTables2, kActionDrawTablesWithChairs, "009G");
+ getSavePoints()->push(kEntityPascale, kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ getSavePoints()->push(kEntityPascale, kEntityTables4, kActionDrawTablesWithChairs, "014F");
+ getSavePoints()->push(kEntityPascale, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Pascale, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(2);
+ setup_updateFromTime(450);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function29();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityPascale);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityCoudert, kAction123712592);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function30();
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 8) = 0;
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 1;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Pascale, messageFromAnna)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("902");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityPascale, "010E2");
+
+ setCallback(2);
+ setup_playSound("Aug4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPascale, kEntityAugust, kAction123793792);
+
+ setCallback(3);
+ setup_draw("905");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityPascale);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Pascale, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_850;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Pascale, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("817US");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityPascale, "817UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityPascale);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Pascale, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityPascale);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Pascale, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function33();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Pascale, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param4) {
+ UPDATE_PARAM_PROC(params->param5, getState()->time, 4500)
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("Wat5010");
+ break;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_callback1:
+ if (params->param1) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 2;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param6 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param1) {
+ params->param1 = 0;
+ params->param2 = 0;
+ params->param3 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ goto label_callback1;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ params->param3++;
+
+ if (params->param3 == 1 || params->param3 == 2) {
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorNormal, kCursorNormal);
+ setCallback(params->param3 == 1 ? 5 : 6);
+ setup_playSound(params->param3 == 1 ? "Wat5001" : "Wat5002");
+ }
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentG, kEntityPascale, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+
+ case 6:
+ params->param2 = 1;
+ break;
+
+ case 7:
+ params->param4 = 1;
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction169750080:
+ if (getSound()->isBuffered(kEntityPascale)) {
+ params->param4 = 1;
+ } else {
+ setCallback(7);
+ setup_playSound("Wat5002");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(34, Pascale)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/pascale.h b/engines/lastexpress/entities/pascale.h
new file mode 100644
index 0000000000..d0098dcf0b
--- /dev/null
+++ b/engines/lastexpress/entities/pascale.h
@@ -0,0 +1,165 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_PASCALE_H
+#define LASTEXPRESS_PASCALE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Pascale : public Entity {
+public:
+ Pascale(LastExpressEngine *engine);
+ ~Pascale() {}
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the position
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The car
+ * - The position
+ */
+ DECLARE_FUNCTION_NOSETUP(updatePosition)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ * - The sequence to draw for the second entity
+ * - The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_NOSETUP(draw2)
+
+ DECLARE_FUNCTION(welcomeSophieAndRebecca)
+ DECLARE_FUNCTION(sitSophieAndRebecca)
+ DECLARE_FUNCTION(welcomeCath)
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(getMessageFromAugustToTyler)
+ DECLARE_FUNCTION(sitAnna)
+ DECLARE_FUNCTION(welcomeAnna)
+ DECLARE_FUNCTION(serveTatianaVassili)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(welcomeAbbot)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(messageFromAnna)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function33)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_PASCALE_H
diff --git a/engines/lastexpress/entities/rebecca.cpp b/engines/lastexpress/entities/rebecca.cpp
new file mode 100644
index 0000000000..e902c5f37b
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.cpp
@@ -0,0 +1,1838 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/rebecca.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Rebecca::Rebecca(LastExpressEngine *engine) : Entity(engine, kEntityRebecca) {
+ ADD_CALLBACK_FUNCTION(Rebecca, reset);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound);
+ ADD_CALLBACK_FUNCTION(Rebecca, playSound16);
+ ADD_CALLBACK_FUNCTION(Rebecca, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Rebecca, enterExitCompartment3);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Rebecca, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Rebecca, updateEntity);
+ ADD_CALLBACK_FUNCTION(Rebecca, updatePosition);
+ ADD_CALLBACK_FUNCTION(Rebecca, draw2);
+ ADD_CALLBACK_FUNCTION(Rebecca, function15);
+ ADD_CALLBACK_FUNCTION(Rebecca, function16);
+ ADD_CALLBACK_FUNCTION(Rebecca, function17);
+ ADD_CALLBACK_FUNCTION(Rebecca, function18);
+ ADD_CALLBACK_FUNCTION(Rebecca, function19);
+ ADD_CALLBACK_FUNCTION(Rebecca, function20);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function23);
+ ADD_CALLBACK_FUNCTION(Rebecca, function24);
+ ADD_CALLBACK_FUNCTION(Rebecca, function25);
+ ADD_CALLBACK_FUNCTION(Rebecca, function26);
+ ADD_CALLBACK_FUNCTION(Rebecca, function27);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function30);
+ ADD_CALLBACK_FUNCTION(Rebecca, function31);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function34);
+ ADD_CALLBACK_FUNCTION(Rebecca, function35);
+ ADD_CALLBACK_FUNCTION(Rebecca, function36);
+ ADD_CALLBACK_FUNCTION(Rebecca, function37);
+ ADD_CALLBACK_FUNCTION(Rebecca, function38);
+ ADD_CALLBACK_FUNCTION(Rebecca, function39);
+ ADD_CALLBACK_FUNCTION(Rebecca, function40);
+ ADD_CALLBACK_FUNCTION(Rebecca, function41);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function44);
+ ADD_CALLBACK_FUNCTION(Rebecca, function45);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5);
+ ADD_CALLBACK_FUNCTION(Rebecca, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Rebecca, function48);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Rebecca, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(2, Rebecca, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Rebecca, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Rebecca, playSound16)
+ Entity::playSound(savepoint, false, getSound()->getSoundFlag(kEntityCoudert));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Rebecca, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Rebecca, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(7, Rebecca, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(8, Rebecca, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_4840, kPosition_4455, kCarRedSleeping, kObjectCompartmentE, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(9, Rebecca, enterExitCompartment3, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Rebecca, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Rebecca, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Rebecca, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(13, Rebecca, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SSI(14, Rebecca, draw2, EntityIndex)
+ Entity::draw2(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Rebecca, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ setCallback(1);
+ setup_enterExitCompartment2("624Ae", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityRebecca);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Rebecca, function16, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)) {
+ if (!getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ }
+ }
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment(params->param1 ? "624Be" : "623Ee", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("810US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityRebecca, "012B");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012A");
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "REB3000");
+
+ getSavePoints()->push(kEntityRebecca, kEntityPascale, kAction269479296);
+
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction157370960:
+ getSavePoints()->push(kEntityRebecca, kEntityTables3, kAction136455232);
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(17, Rebecca, function17, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ && !getEntities()->hasValidFrame(kEntitySophie)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(3);
+ setup_updateFromTime(0);
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction125242096);
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getProgress().chapter == kChapter3)
+ getSound()->playSound(kEntityRebecca, "Reb3005", SoundManager::kFlagInvalid, 75);
+
+ if (params->param1) {
+ setCallback(5);
+ setup_updatePosition("118A", kCarRestaurant, 52);
+ } else {
+ getEntities()->updatePositionEnter(kEntityRebecca, kCarRestaurant, 57);
+
+ setCallback(6);
+ setup_draw2("107A1", "107A2", kEntitySophie);
+ }
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ getEntities()->updatePositionExit(kEntityRebecca, kCarRestaurant, 57);
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->location = kLocationInsideCompartment;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Rebecca, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(3);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(2);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 2:
+ case 3:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Rebecca, function19)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ getEntities()->exitCompartment(kEntityRebecca, kObjectCompartmentE, true);
+
+ setCallback(6);
+ setup_function15();
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_callSavepoint("012H", kEntityTables3, kActionDrawTablesWithChairs, "010M");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction337548856);
+ getEntities()->drawSequenceRight(kEntityRebecca, "810DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityRebecca);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction136654208);
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 4:
+ if (getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntitySophie, 750)
+ || getEntities()->checkDistanceFromPosition(kEntitySophie, kPosition_4840, 500)) {
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+
+ setCallback(5);
+ setup_function15();
+ } else {
+ getEntities()->drawSequenceLeft(kEntityRebecca, "623Ge");
+ getEntities()->enterCompartment(kEntityRebecca, kObjectCompartmentE, true);
+ }
+ break;
+
+ case 5:
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(20, Rebecca, function20, TimeValue)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param5) {
+ params->param5 = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (!params->param2) {
+ params->param6 = 0;
+ } else {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ params->param2 = 0;
+ params->param3 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (getProgress().chapter == kChapter1 && !ENTITY_PARAM(0, 3)) {
+ if (params->param7 != kTimeInvalid && getState()->time > kTime1174500) {
+ if (getState()->time <= kTime1183500) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 2000) || getSound()->isBuffered("CON1210") || !params->param7)
+ params->param7 = (uint)(getState()->time);
+
+ if (params->param7 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param7 = kTimeInvalid;
+ ENTITY_PARAM(0, 3) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_playSound("REB1205");
+ break;
+ }
+ goto label_callback;
+ }
+
+ if (getProgress().chapter == kChapter3 && !ENTITY_PARAM(0, 4) && params->param8 != kTimeInvalid && getState()->time > kTime2097000) {
+ if (getState()->time <= kTime2106000) {
+ if (!getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000) || !params->param8)
+ params->param8 = (uint)getState()->time;
+
+ if (params->param8 >= getState()->time)
+ goto label_callback;
+ }
+
+ params->param8 = kTimeInvalid;
+ ENTITY_PARAM(0, 4) = 1;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(2);
+ setup_playSound("REB3010");
+ break;
+ }
+
+label_callback:
+ if (ENTITY_PARAM(0, 2) && getEntities()->isDistanceBetweenEntities(kEntityRebecca, kEntityPlayer, 1000)) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(3);
+ setup_playSound("REB1040");
+ }
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ break;
+
+ case kActionDrawScene:
+ if (params->param3 || params->param2) {
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getCallback() != 2)
+ ENTITY_PARAM(0, 2) = 0;
+
+ if (getCallback() != 3)
+ goto label_callback;
+ break;
+
+ case 4:
+ case 5:
+ if (rnd(2)) {
+ setCallback(6);
+ setup_playSound("REB1039");
+ } else {
+ setCallback(7);
+ setup_playSound(rnd(2) ? "SOP1039" : "SOP1039A");
+ }
+ break;
+
+ case 6:
+ case 7:
+ params->param4 = (getCallback() == 6 ? 0 : 1);
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+
+ case 12:
+ setCallback(13);
+ setup_playSound16("JAC1012B");
+ break;
+
+ case 13:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction254915200:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject52, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(12);
+ setup_playSound("REB1039A");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Rebecca, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityRebecca, kAction224253538, 0);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectOutsideBetweenCompartments, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation1);
+
+ getData()->entityPosition = kPosition_2830;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Rebecca, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_1(kTime1084500, params->param3, 1, setup_playSound, "REB1015");
+
+ if (params->param4 == kTimeInvalid)
+ goto label_callback_4;
+
+ if (getState()->time > kTime1080000)
+ goto label_playConversation;
+
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)(getState()->time + 150);
+
+ if (params->param4 >= getState()->time) {
+label_callback_4:
+ if (params->param1) {
+ UPDATE_PARAM_CHECK(params->param5, getState()->time, 900)
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("REB1013");
+ break;
+ }
+ }
+ }
+
+label_callback_5:
+ if (params->param2) {
+ UPDATE_PARAM(params->param6, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 55);
+ } else {
+ params->param6 = 0;
+ }
+ } else {
+label_playConversation:
+ params->param4 = kTimeInvalid;
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_B8 = 1;
+
+ setCallback(4);
+ setup_playSound("REB1012");
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionDrawScene:
+ params->param2 = (getEntities()->isPlayerPosition(kCarRestaurant, 57) ? 1 : 0);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updatePosition("107C", kCarRestaurant, 57);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ setup_function23();
+ break;
+
+ case 4:
+ params->param1 = 1;
+ goto label_callback_4;
+
+ case 5:
+ getProgress().field_B4 = 1;
+ params->param1 = 0;
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Rebecca, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_2(kTime1111500, params->param2, 3, setup_enterExitCompartment, "623De", kObjectCompartmentE);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623Ce", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+
+ case 3:
+ case 6:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback((byte)(getCallback() + 1));
+ setup_function20(kTime1120500);
+ break;
+
+ case 4:
+ case 5:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(5);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+
+ case 7:
+ case 8:
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function24();
+ } else {
+ setCallback(8);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ break;
+ }
+ break;
+
+ case kAction285528346:
+ setCallback(6);
+ setup_enterExitCompartment("623De", kObjectCompartmentE);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Rebecca, function24)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_SAVEPOINT(kTime1134000, params->param2, kEntityRebecca, kEntityServers0, kAction223712416);
+
+ if (!params->param1)
+ break;
+
+ TIME_CHECK_CALLBACK(kTime1165500, params->param3, 6, setup_function19);
+
+ if (params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime1161000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(7);
+ setup_playSound("REB1200A");
+ }
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+
+ setCallback(2);
+ setup_playSound("REB1199");
+ break;
+
+ case 2:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(3);
+ setup_playSound("REB1199A");
+ break;
+ }
+ // Fallback to next case
+
+ case 3:
+ if (getCallback() == 3)
+ getProgress().field_BC = 1;
+
+ if (getEntities()->isInRestaurant(kEntityAnna)) {
+ setCallback(4);
+ setup_playSound("REB1199B");
+ break;
+ }
+ // Fallback to next case
+
+ case 4:
+ setCallback(5);
+ setup_playSound("REB1199C");
+ break;
+
+ case 6:
+ setup_function25();
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012E");
+
+ setCallback(8);
+ setup_playSound("REB1200");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Rebecca, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1184400);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function26();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Rebecca, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime1224000, params->param2, 1, setup_updatePosition, "118H", kCarRestaurant, 52);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 90);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 52);
+ params->param3 = 0;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case 2:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Rebecca, function27)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Rebecca, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation2);
+
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Rebecca, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime1764000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function17(false);
+ break;
+
+ case 2:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Rebecca, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && params->param4 != kTimeInvalid) {
+
+ if (getState()->time <= kTimeEnd)
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 450;
+
+ if (params->param4 < getState()->time || getState()->time > kTimeEnd) {
+ params->param4 = kTimeInvalid;
+
+ getSound()->playSound(kEntityRebecca, "Reb2001");
+ getProgress().field_B0 = 1;
+ params->param2 = 1;
+ }
+ }
+
+ if (!params->param3 && !params->param2 && params->param5 != kTimeInvalid) {
+
+ if (getState()->time <= kTime10881000) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time + 450;
+
+ if (params->param5 >= getState()->time)
+ break;
+ }
+
+ params->param5 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityAugust, kAction169358379);
+ }
+ break;
+
+ case kActionEndSound:
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "107B");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function31();
+ break;
+
+ case kAction125496184:
+ setCallback(1);
+ setup_function18();
+ break;
+
+ case kAction155465152:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ break;
+
+ case kAction155980128:
+ params->param1 = 1;
+ params->param3 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Rebecca, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateFromTime(900);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("623CE", kObjectCompartmentE);
+ break;
+
+ case 2:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation2, kCursorNormal, kCursorNormal);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "504");
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Rebecca, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Rebecca, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2016000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function34();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Rebecca, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2 == kTimeInvalid) {
+ if (getState()->time <= kTime1386000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param2)
+ params->param2 = (uint)getState()->time;
+
+ if (params->param2 >= getState()->time) {
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+ }
+ }
+
+ params->param2 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+ TIME_CHECK_CALLBACK(kTime2052000, params->param3, 1, setup_function19);
+ break;
+
+ case kActionEndSound:
+ setCallback(5);
+ setup_playSound("Reb3004");
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(2);
+ setup_playSound("Reb3002");
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param1 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb3003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Rebecca, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2070000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function36();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Rebecca, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2)
+ params->param2 = (uint)getState()->time + 1800;
+
+ if (params->param4 != kTimeInvalid && params->param2 < getState()->time) {
+
+ if (getState()->time <= kTime2083500) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 300;
+ }
+
+ if (params->param4 < getState()->time || getState()->time > kTime2083500) {
+ params->param4 = kTimeInvalid;
+ getSound()->playSound(kEntityRebecca, "Reb3007");
+
+ setCallback(2);
+ setup_updatePosition("118E", kCarRedSleeping, 52);
+ break;
+ }
+ }
+
+ // TODO rewrite using proper if/else blocks instead of goto
+label_callback_2:
+ if (!params->param1)
+ goto label_callback_3;
+
+ if (!params->param3)
+ params->param3 = (uint)getState()->time + 9000;
+
+ if (params->param5 == kTimeInvalid || params->param3 >= getState()->time)
+ goto label_callback_3;
+
+ if (getState()->time <= kTime2092500) {
+ if (!getEntities()->isInSalon(kEntityPlayer) || !params->param5)
+ params->param5 = (uint)getState()->time + 300;
+
+ if (params->param5 >= getState()->time) {
+label_callback_3:
+ if (getState()->time > kTime2097000 && !params->param6) {
+ params->param6 = 1;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_updatePosition("118H", kCarRestaurant, 52);
+ }
+ break;
+ }
+ }
+
+ params->param5 = kTimeInvalid;
+
+ getData()->inventoryItem = kItemNone;
+ getSound()->playSound(kEntityRebecca, "Reb3008", SoundManager::kFlagInvalid, 60);
+ getEntities()->updatePositionEnter(kEntityRebecca, kCarRestaurant, 52);
+
+ setCallback(3);
+ setup_draw2("118G1", "118G2", kEntitySophie);
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(6);
+ setup_playSound("SOP3008");
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function17(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ break;
+
+ case 2:
+ params->param1 = 1;
+ getData()->inventoryItem = kItemInvalid;
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118F");
+ goto label_callback_2;
+
+ case 3:
+ getEntities()->clearSequences(kEntitySophie);
+ getEntities()->updatePositionExit(kEntityRebecca, kCarRestaurant, 52);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "118D");
+ goto label_callback_3;
+
+ case 4:
+ setCallback(5);
+ setup_function18();
+ break;
+
+ case 5:
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Rebecca, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2110500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function38();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Rebecca, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment3("624Be", kObjectCompartmentE);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction259921280);
+
+ setCallback(2);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setup_function39();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Rebecca, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarKronos;
+ break;
+
+ case kAction191668032:
+ setup_function40();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Rebecca, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_9270;
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction292775040);
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityRebecca, kEntityAnna, kAction191668032);
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityRebecca, kEntitySophie, kAction123668192);
+ setCallback(4);
+ setup_function15();
+ break;
+
+ case 4:
+ setup_function41();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Rebecca, function41)
+ if (savepoint.action == kActionDefault) {
+ ENTITY_PARAM(0, 2) = 1;
+
+ setCallback(1);
+ setup_function20(kTimeEnd);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Rebecca, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation3);
+
+ ENTITY_PARAM(0, 1) = 0;
+ ENTITY_PARAM(0, 2) = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Rebecca, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function20(kTime2385000);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ if (ENTITY_PARAM(0, 1)) {
+ setup_function44();
+ } else {
+ setCallback(2);
+ setup_function20((TimeValue)(getState()->time + 900));
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Rebecca, function44)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param3 != kTimeInvalid) {
+ if (getState()->time <= kTime2412000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param3)
+ params->param3 = (uint)getState()->time;
+
+ if (params->param3 >= getState()->time)
+ goto label_next;
+ }
+
+ params->param3 = kTimeInvalid;
+
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction223712416);
+ }
+
+label_next:
+ if (params->param1 && params->param4 != kTimeInvalid) {
+ if (getState()->time <= kTime2430000) {
+ if (!getEntities()->isInRestaurant(kEntityPlayer) || !params->param4)
+ params->param4 = (uint)getState()->time + 150;
+
+ if (params->param4 >= getState()->time)
+ goto label_callback_2;
+ }
+
+ params->param4 = kTimeInvalid;
+
+ setCallback(2);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+label_callback_2:
+ if (params->param2)
+ TIME_CHECK_CALLBACK(kTime2443500, params->param5, 3, setup_function19);
+ break;
+
+ case kActionEndSound:
+ if (getEntities()->isInRestaurant(kEntityPlayer)) {
+ setCallback(5);
+ setup_playSound("Reb4004");
+ break;
+ }
+
+ params->param1 = 1;
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_function16(true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012D");
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setup_function45();
+ break;
+
+ case 4:
+ getSavePoints()->push(kEntityRebecca, kEntityServers0, kAction136702400);
+ getEntities()->drawSequenceLeft(kEntityRebecca, "012G");
+ params->param2 = 1;
+ break;
+ }
+ break;
+
+ case kAction123712592:
+ getEntities()->drawSequenceLeft(kEntityRebecca, "BLANK");
+ getSound()->playSound(kEntityRebecca, "Reb4003");
+
+ setCallback(4);
+ setup_draw("012E");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Rebecca, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject52, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getEntities()->clearSequences(kEntityRebecca);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction205034665:
+ if (!params->param1 && getState()->time < kTime2511000) {
+ setCallback(1);
+ setup_playSound("Reb6969");
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Rebecca, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityRebecca);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->updateLocation2(kObject110, kObjectLocation4);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Rebecca, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function48();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Rebecca, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 75);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param3 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (params->param1) {
+ params->param1 = 0;
+
+ setCallback(2);
+ setup_playSound(getSound()->justCheckingCath());
+ } else {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_enterExitCompartment("624AE", kObjectCompartmentE);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ params->param1 = 0;
+ params->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityRebecca);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_4840;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case 3:
+ case 4:
+ setCallback(5);
+ setup_playSound("Reb5001");
+ break;
+
+ case 5:
+ params->param1 = 1;
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorTalk, kCursorNormal);
+ break;
+ }
+ break;
+
+ case kAction135800432:
+ setup_nullfunction();
+ break;
+
+ case kAction155604840:
+ getObjects()->update(kObjectCompartmentE, kEntityRebecca, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(49, Rebecca)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/rebecca.h b/engines/lastexpress/entities/rebecca.h
new file mode 100644
index 0000000000..bdc9fe1d3b
--- /dev/null
+++ b/engines/lastexpress/entities/rebecca.h
@@ -0,0 +1,230 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_REBECCA_H
+#define LASTEXPRESS_REBECCA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Rebecca : public Entity {
+public:
+ Rebecca(LastExpressEngine *engine);
+ ~Rebecca() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound16, const char *filename)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment3, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Draws the entity along with another one
+ *
+ * @param sequence1 The sequence to draw
+ * @param sequence2 The sequence to draw for the second entity
+ * @param entity The EntityIndex of the second entity
+ */
+ DECLARE_FUNCTION_3(draw2, const char *sequence1, const char *sequence2, EntityIndex entity)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, bool)
+ DECLARE_FUNCTION_1(function17, bool)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION_1(function20, TimeValue timeValue)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_FUNCTION(function31)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function44)
+ DECLARE_FUNCTION(function45)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function48)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_REBECCA_H
diff --git a/engines/lastexpress/entities/salko.cpp b/engines/lastexpress/entities/salko.cpp
new file mode 100644
index 0000000000..cddbc9005d
--- /dev/null
+++ b/engines/lastexpress/entities/salko.cpp
@@ -0,0 +1,642 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/salko.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Salko::Salko(LastExpressEngine *engine) : Entity(engine, kEntitySalko) {
+ ADD_CALLBACK_FUNCTION(Salko, reset);
+ ADD_CALLBACK_FUNCTION(Salko, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Salko, draw);
+ ADD_CALLBACK_FUNCTION(Salko, updateEntity);
+ ADD_CALLBACK_FUNCTION(Salko, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Salko, savegame);
+ ADD_CALLBACK_FUNCTION(Salko, function7);
+ ADD_CALLBACK_FUNCTION(Salko, function8);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1);
+ ADD_CALLBACK_FUNCTION(Salko, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function11);
+ ADD_CALLBACK_FUNCTION(Salko, chapter2);
+ ADD_CALLBACK_FUNCTION(Salko, function13);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3);
+ ADD_CALLBACK_FUNCTION(Salko, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function16);
+ ADD_CALLBACK_FUNCTION(Salko, function17);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4);
+ ADD_CALLBACK_FUNCTION(Salko, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Salko, function20);
+ ADD_CALLBACK_FUNCTION(Salko, function21);
+ ADD_CALLBACK_FUNCTION(Salko, function22);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5);
+ ADD_CALLBACK_FUNCTION(Salko, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Salko, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Salko, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(3, Salko, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(4, Salko, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(5, Salko, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(6, Salko, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Salko, function7, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ EntityDirection direction = getData()->direction;
+ CarIndex carSalko = getData()->car;
+ CarIndex carIvo = getEntityData(kEntityIvo)->car;
+ EntityPosition positionSalko = getData()->entityPosition;
+ EntityPosition positionIvo = getEntityData(kEntityIvo)->entityPosition;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityIvo, 500)
+ || (direction == kDirectionUp && (carSalko > carIvo || (carSalko == carIvo && positionSalko > positionIvo)))
+ || (direction == kDirectionDown && (carSalko < carIvo || (carSalko == carIvo && positionSalko < positionIvo)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ }
+ break;
+
+ case kActionExcuseMeCath:
+ case kActionExcuseMe:
+ getSound()->playSound(kEntityPlayer, "ZFX1002", getSound()->getSoundFlag(kEntitySalko));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySalko, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Salko, function8)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Salko, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4691;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Salko, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityIvo)->entityPosition;
+ getData()->location = getEntityData(kEntityIvo)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function8();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Salko, function11)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Salko, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kAction136184016:
+ setCallback(1);
+ setup_function13();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Salko, function13)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntitySalko, kEntityIvo, kAction102675536);
+ getEntities()->clearSequences(kEntitySalko);
+ break;
+
+ case 3:
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(3);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Salko, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Salko, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time < kTime2200500) {
+ UPDATE_PARAM(params->param1, getState()->time, 81000);
+
+ setCallback(1);
+ setup_function16();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Salko, function16)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->hasValidFrame(kEntitySalko) && getEntities()->isDistanceBetweenEntities(kEntitySalko, kEntityPlayer, 5000)) {
+ getSavePoints()->push(kEntitySalko, kEntityMax, kAction158007856);
+
+ setCallback(3);
+ setup_updateFromTime(75);
+ break;
+ }
+
+label_callback3:
+ UPDATE_PARAM(params->param1, getState()->time, 4500);
+
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("612DH", kObjectCompartmentH);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntitySalko, kEntitySalko, kAction101169464);
+ goto label_callback3;
+
+ case 4:
+ getEntities()->exitCompartment(kEntitySalko, kObjectCompartmentF, true);
+
+ setCallback(5);
+ setup_updateEntity(kCarRedSleeping, kPosition_9460);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 8:
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_2740;
+ getEntities()->clearSequences(kEntitySalko);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction101169464:
+ setCallback(4);
+ setup_enterExitCompartment("612Bf", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Salko, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_6470;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2740);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getSavePoints()->push(kEntitySalko, kEntityMilos, kAction157691176);
+
+ setup_chapter3Handler();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Salko, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Salko, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntitySalko, "BLANK");
+
+ getData()->location = kLocationInsideCompartment;
+
+ setup_function20();
+ }
+ break;
+
+ case kAction125242096:
+ setCallback(1);
+ setup_function7(kCarRedSleeping, kPosition_2740);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Salko, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntitySalko);
+ setup_function21();
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ setCallback(1);
+ setup_enterExitCompartment("612Dh", kObjectCompartmentH);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Salko, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2422800 && !params->param1) {
+ params->param1 = 1;
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_2740);
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment("612Ch", kObjectCompartmentH);
+ break;
+
+ case 2:
+ setup_function22();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Salko, function22)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntitySalko);
+ getObjects()->update(kObjectCompartmentH, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_2740;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Salko, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySalko);
+
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Salko, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getAction()->playAnimation(kEventCathSalkoTrainTopFight);
+
+ setCallback(2);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 2:
+ params->param1 = getFight()->setup(kFightSalko);
+
+ if (params->param1 == Fight::kFightEndWin) {
+ getState()->time = (TimeValue)(getState()->time + 1800);
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopWin);
+ } else {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param1 == Fight::kFightEndLost);
+ }
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathSalkoTrainTopWin);
+ getSavePoints()->push(kEntitySalko, kEntityVesna, kAction134427424);
+
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 10);
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathSalkoTrainTopFight);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(25, Salko)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/salko.h b/engines/lastexpress/entities/salko.h
new file mode 100644
index 0000000000..f21724d88a
--- /dev/null
+++ b/engines/lastexpress/entities/salko.h
@@ -0,0 +1,149 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SALKO_H
+#define LASTEXPRESS_SALKO_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Salko : public Entity {
+public:
+ Salko(LastExpressEngine *engine);
+ ~Salko() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Draws the entity
+ *
+ * @param savepoint The savepoint
+ * - The sequence to draw
+ */
+ DECLARE_FUNCTION_NOSETUP(draw)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION_2(function7, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function8)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SALKO_H
diff --git a/engines/lastexpress/entities/servers0.cpp b/engines/lastexpress/entities/servers0.cpp
new file mode 100644
index 0000000000..1f3f85bb8f
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.cpp
@@ -0,0 +1,1039 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/servers0.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+#define HANDLE_TABLE(index, param, callback, function) \
+ if (ENTITY_PARAM(index, param)) { \
+ setCallback(callback); \
+ function(); \
+ break; \
+ }
+
+Servers0::Servers0(LastExpressEngine *engine) : Entity(engine, kEntityServers0) {
+ ADD_CALLBACK_FUNCTION(Servers0, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers0, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers0, draw);
+ ADD_CALLBACK_FUNCTION(Servers0, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers0, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers0, playSound);
+ ADD_CALLBACK_FUNCTION(Servers0, function7);
+ ADD_CALLBACK_FUNCTION(Servers0, function8);
+ ADD_CALLBACK_FUNCTION(Servers0, function9);
+ ADD_CALLBACK_FUNCTION(Servers0, function10);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers0, function12);
+ ADD_CALLBACK_FUNCTION(Servers0, function13);
+ ADD_CALLBACK_FUNCTION(Servers0, function14);
+ ADD_CALLBACK_FUNCTION(Servers0, function15);
+ ADD_CALLBACK_FUNCTION(Servers0, function16);
+ ADD_CALLBACK_FUNCTION(Servers0, function17);
+ ADD_CALLBACK_FUNCTION(Servers0, function18);
+ ADD_CALLBACK_FUNCTION(Servers0, function19);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function21);
+ ADD_CALLBACK_FUNCTION(Servers0, function22);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, function25);
+ ADD_CALLBACK_FUNCTION(Servers0, function26);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustAnnaDateOrder);
+ ADD_CALLBACK_FUNCTION(Servers0, function30);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers0, augustOrderSteak);
+ ADD_CALLBACK_FUNCTION(Servers0, augustServeDuck);
+ ADD_CALLBACK_FUNCTION(Servers0, function35);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers0, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(1, Servers0, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(2, Servers0, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Servers0, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Servers0, updatePosition)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Servers0, callbackActionOnDirection)
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers0);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers0, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers0, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ ENTITY_PARAM(0, 3) = 0;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityServers0);
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction123712592);
+ break;
+
+ case 2:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(2);
+ setup_draw("913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers0, function8)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(1, 2));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers0, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("915");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound(getProgress().chapter == kChapter3 ? "Abb3016" : "Abb4001");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("917");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers0, function10)
+ serveTable(savepoint, "916", kEntityTables4, "014E", "014F", "918", &ENTITY_PARAM(2, 3), false);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers0, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers0, kAction270410280, 0);
+ getSavePoints()->addData(kEntityServers0, kAction304061224, 1);
+ getSavePoints()->addData(kEntityServers0, kAction252568704, 10);
+ getSavePoints()->addData(kEntityServers0, kAction286534136, 11);
+ getSavePoints()->addData(kEntityServers0, kAction218983616, 12);
+ getSavePoints()->addData(kEntityServers0, kAction218586752, 13);
+ getSavePoints()->addData(kEntityServers0, kAction207330561, 14);
+ getSavePoints()->addData(kEntityServers0, kAction286403504, 16);
+ getSavePoints()->addData(kEntityServers0, kAction218128129, 17);
+ getSavePoints()->addData(kEntityServers0, kAction270068760, 18);
+ getSavePoints()->addData(kEntityServers0, kAction223712416, 2);
+ getSavePoints()->addData(kEntityServers0, kAction237485916, 5);
+ getSavePoints()->addData(kEntityServers0, kAction188893625, 8);
+ getSavePoints()->addData(kEntityServers0, kAction204704037, 6);
+ getSavePoints()->addData(kEntityServers0, kAction292758554, 7);
+ getSavePoints()->addData(kEntityServers0, kAction337548856, 9);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers0, function12)
+ handleServer(savepoint, "907", kEntityAnna, kAction268773672, &ENTITY_PARAM(0, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers0, function13)
+ handleServer(savepoint, "911", kEntityAugust, kAction268773672, &ENTITY_PARAM(0, 2), "010F");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers0, function14)
+ handleServer(savepoint, "908", kEntityAnna, kAction170016384, &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers0, function15)
+ handleServer(savepoint, "912", kEntityAugust, kAction170016384, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers0, function16)
+ serveTable(savepoint, "907", kEntityTables0, "001N", "001P", "909", &ENTITY_PARAM(0, 6));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers0, function17)
+ serveTable(savepoint, "915", kEntityTables4, "014E", "014F", "917", &ENTITY_PARAM(1, 1), true, false, 67);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers0, function18)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010H", "913", &ENTITY_PARAM(0, 7));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers0, function19)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "913", &ENTITY_PARAM(0, 8), true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers0, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 2700);
+ ENTITY_PARAM(0, 4) = 1;
+ params->param2 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM_PROC(params->param4, getState()->time, 4500)
+ ENTITY_PARAM(0, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers0) && !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(0, 1, 1, setup_function12);
+ HANDLE_TABLE(0, 2, 2, setup_function13);
+ HANDLE_TABLE(0, 3, 3, setup_function7);
+ HANDLE_TABLE(0, 4, 4, setup_function14);
+ HANDLE_TABLE(0, 5, 5, setup_function15);
+ HANDLE_TABLE(0, 6, 6, setup_function16);
+ HANDLE_TABLE(1, 1, 7, setup_function17);
+ HANDLE_TABLE(0, 7, 8, setup_function18);
+ HANDLE_TABLE(0, 8, 9, setup_function19);
+ HANDLE_TABLE(1, 2, 10, setup_function8);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityServers0, kEntityPascale, kAction352703104);
+ setup_function21();
+ break;
+
+ case 11:
+ case 12:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+
+ if (getCallback() == 11)
+ params->param2 = 1;
+ else
+ params->param1 = 1;
+ break;
+
+ case 13:
+ case 14:
+ getEntities()->clearSequences(kEntityServers0);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction136702400:
+ setCallback(savepoint.entity2 == kEntityAnna ? 13 : 14);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "909" : "913");
+ break;
+
+ case kAction203859488:
+ setCallback(savepoint.entity2 == kEntityAnna ? 11 : 12);
+ setup_draw(savepoint.entity2 == kEntityAnna ? "910" : "913");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers0, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function22();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers0, function22)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers0);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers0, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers0, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers0) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ HANDLE_TABLE(1, 3, 1, setup_function25);
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ HANDLE_TABLE(1, 4, 2, setup_function26);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers0, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("957");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers0, "BLANK");
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 3) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+
+ case kAction219522616:
+ setCallback(2);
+ setup_draw("959");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers0, function26)
+ serveTable(savepoint, "957", kEntityTables0, "016E", "016D", "959", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers0, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ ENTITY_PARAM(1, 6) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ ENTITY_PARAM(2, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers0, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(1);
+ setup_augustAnnaDateOrder();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 6)) {
+ setCallback(2);
+ setup_function9();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 4)) {
+ setCallback(3);
+ setup_function30();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(4);
+ setup_function10();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(5);
+ setup_function7();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(6);
+ setup_function8();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers0, augustAnnaDateOrder)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "026D");
+
+ setCallback(2);
+ setup_playSound("Ann3138");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAnna, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 5) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers0, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("916");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122358304);
+ getEntities()->drawSequenceLeft(kEntityServers0, "029D");
+
+ setCallback(2);
+ setup_playSound("Abb3016a");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAbbot, kAction122288808);
+
+ setCallback(3);
+ setup_draw("918");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(2, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers0, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 2) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Servers0, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 3600)
+ ENTITY_PARAM(1, 8) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 7)) {
+ setCallback(1);
+ setup_augustOrderSteak();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(1, 8)) {
+ setCallback(2);
+ setup_augustServeDuck();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(2, 1)) {
+ setCallback(3);
+ setup_function35();
+ break;
+ }
+
+label_callback_3:
+ if (ENTITY_PARAM(2, 2)) {
+ setCallback(4);
+ setup_function9();
+ break;
+ }
+
+label_callback_4:
+ if (ENTITY_PARAM(2, 3)) {
+ setCallback(5);
+ setup_function10();
+ break;
+ }
+
+label_callback_5:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(6);
+ setup_function7();
+ break;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ params->param1 = 1;
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+ }
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(1, 7) = 0;
+ ENTITY_PARAM(1, 8) = 0;
+ ENTITY_PARAM(2, 1) = 0;
+ ENTITY_PARAM(2, 3) = 0;
+ params->param1 = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Servers0, augustOrderSteak)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("911");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityServers0, "010F3");
+ getEntities()->drawSequenceLeft(kEntityAugust, "010D3");
+
+ setCallback(2);
+ setup_playSound("AUG4002");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122288808);
+
+ setCallback(3);
+ setup_draw("913");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Servers0, augustServeDuck)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_draw("912");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction122358304);
+ getSound()->playSound(kEntityServers0, "AUG1053");
+
+ setCallback(2);
+ setup_draw("010G3");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers0, kEntityAugust, kAction201964801);
+
+ setCallback(3);
+ setup_draw("914");
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers0);
+ ENTITY_PARAM(1, 8) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Servers0, function35)
+ serveTable(savepoint, "911", kEntityTables3, "010L", "010M", "914", &ENTITY_PARAM(2, 1), false, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Servers0, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers0);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Servers0, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(38, Servers0)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers0::handleServer(const SavePoint &savepoint, const char *name, EntityIndex entity, ActionIndex action, uint *parameter, const char *name2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw(name);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ // Prepare or draw sequences depending of value of string
+ if (strcmp(name2, ""))
+ getEntities()->clearSequences(kEntityServers0);
+ else
+ getEntities()->drawSequenceLeft(kEntityServers0, name2);
+
+ getSavePoints()->push(kEntityServers0, entity, action);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers0::serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, bool shouldUpdatePosition, bool pushSavepoint, Position position) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers0, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers0, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers0, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+
+ // Special case for functions 19 & 35
+ if (pushSavepoint)
+ getSavePoints()->push(kEntityServers0, kEntityRebecca, kAction224253538);
+
+ getEntities()->clearSequences(kEntityServers0);
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers0.h b/engines/lastexpress/entities/servers0.h
new file mode 100644
index 0000000000..3e1bd21e0d
--- /dev/null
+++ b/engines/lastexpress/entities/servers0.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SERVERS0_H
+#define LASTEXPRESS_SERVERS0_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers0 : public Entity {
+public:
+ Servers0(LastExpressEngine *engine);
+ ~Servers0() {}
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION_NOSETUP(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+ DECLARE_FUNCTION(function17)
+ DECLARE_FUNCTION(function18)
+ DECLARE_FUNCTION(function19)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(augustAnnaDateOrder)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(augustOrderSteak)
+ DECLARE_FUNCTION(augustServeDuck)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void handleServer(const SavePoint &savepoint, const char *name, EntityIndex entity, ActionIndex action, uint *parameter, const char *name2 = "");
+ void serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, bool shouldUpdatePosition = true, bool pushSavepoint = false, Position position = 0);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS0_H
diff --git a/engines/lastexpress/entities/servers1.cpp b/engines/lastexpress/entities/servers1.cpp
new file mode 100644
index 0000000000..1afeeab894
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.cpp
@@ -0,0 +1,787 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/servers1.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Servers1::Servers1(LastExpressEngine *engine) : Entity(engine, kEntityServers1) {
+ ADD_CALLBACK_FUNCTION(Servers1, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Servers1, draw);
+ ADD_CALLBACK_FUNCTION(Servers1, updatePosition);
+ ADD_CALLBACK_FUNCTION(Servers1, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Servers1, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Servers1, playSound);
+ ADD_CALLBACK_FUNCTION(Servers1, function7);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1);
+ ADD_CALLBACK_FUNCTION(Servers1, function9);
+ ADD_CALLBACK_FUNCTION(Servers1, function10);
+ ADD_CALLBACK_FUNCTION(Servers1, function11);
+ ADD_CALLBACK_FUNCTION(Servers1, function12);
+ ADD_CALLBACK_FUNCTION(Servers1, function13);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function15);
+ ADD_CALLBACK_FUNCTION(Servers1, function16);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function19);
+ ADD_CALLBACK_FUNCTION(Servers1, function20);
+ ADD_CALLBACK_FUNCTION(Servers1, function21);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function24);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Servers1, function27);
+ ADD_CALLBACK_FUNCTION(Servers1, function28);
+ ADD_CALLBACK_FUNCTION(Servers1, function29);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5);
+ ADD_CALLBACK_FUNCTION(Servers1, chapter5Handler);
+ ADD_NULL_FUNCTION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(1, Servers1, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Servers1, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(3, Servers1, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Servers1, callbackActionOnDirection)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityServers1);
+ params->param1 = 1;
+ }
+ }
+
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(5, Servers1, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(6, Servers1, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Servers1, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122358304);
+ setCallback(2);
+ setup_draw("008C");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction122288808);
+ setCallback(2);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(1, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Servers1, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter1Handler();
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityServers1, kAction223002560, 0);
+ getSavePoints()->addData(kEntityServers1, kAction302996448, 2);
+ getSavePoints()->addData(kEntityServers1, kAction269485588, 3);
+ getSavePoints()->addData(kEntityServers1, kAction326144276, 4);
+ getSavePoints()->addData(kEntityServers1, kAction302203328, 5);
+ getSavePoints()->addData(kEntityServers1, kAction189688608, 6);
+ getSavePoints()->addData(kEntityServers1, kAction236237423, 7);
+ getSavePoints()->addData(kEntityServers1, kAction219377792, 8);
+ getSavePoints()->addData(kEntityServers1, kAction256200848, 9);
+ getSavePoints()->addData(kEntityServers1, kAction291721418, 10);
+ getSavePoints()->addData(kEntityServers1, kAction258136010, 11);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Servers1, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityMilos, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "009B");
+
+ setCallback(2);
+ setup_playSound("WAT1001");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityMilos, "009A");
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 1) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Servers1, function10)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("924");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityBoutarel, "BLANK");
+ getEntities()->drawSequenceLeft(kEntityServers1, "008C");
+
+ setCallback(2);
+ setup_playSound("MRB1077");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, kEntityBoutarel, kAction168717392);
+
+ setCallback(3);
+ setup_draw("926");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ ENTITY_PARAM(0, 2) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Servers1, function11)
+ serveTable(savepoint, "919", kEntityTables1, "005H", "005J", "921", &ENTITY_PARAM(0, 3), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Servers1, function12)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Servers1, function13)
+ serveTable(savepoint, "923", kEntityTables2, "009F", "009G", "926", &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Servers1, chapter1Handler)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 1)) {
+ setCallback(1);
+ setup_function9();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function10();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function11();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(4);
+ setup_function12();
+ break;
+ }
+
+ if (ENTITY_PARAM(0, 5)) {
+ setCallback(5);
+ setup_function13();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 5) {
+ getSavePoints()->push(kEntityServers1, kEntityPascale, kAction352768896);
+ setup_function15();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Servers1, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5900;
+ break;
+
+ case kAction101632192:
+ setup_function16();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Servers1, function16)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+
+ getEntities()->clearSequences(kEntityServers1);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Servers1, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 6) = 0;
+ ENTITY_PARAM(0, 7) = 0;
+ ENTITY_PARAM(0, 8) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Servers1, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(0, 6)) {
+ setCallback(1);
+ setup_function19();
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(2);
+ setup_function20();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 8) || ENTITY_PARAM(0, 5)) {
+ setCallback(3);
+ setup_function21();
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 4:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ break;
+ }
+ break;
+
+ case kAction101106391:
+ setCallback(4);
+ setup_draw("975");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Servers1, function19)
+ serveTable(savepoint, "969", kEntityTables1, "005H2", "018A", "971", &ENTITY_PARAM(0, 6), 63);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Servers1, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("973");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityServers1, kEntityIvo, kAction123712592);
+ getEntities()->drawSequenceLeft(kEntityServers1, "BLANK");
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Servers1, function21)
+ serveTable(savepoint, "974", kEntityTables2, "009F2", "009G", "976", &ENTITY_PARAM(0, 8), 0, true, &ENTITY_PARAM(0, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Servers1, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes1;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(1, 1) = 0;
+ ENTITY_PARAM(1, 2) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Servers1, chapter3Handler)
+ if (savepoint.action != kActionNone)
+ return;
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ return;
+
+ if (ENTITY_PARAM(1, 1)) {
+ setCallback(1);
+ setup_function24();
+ return;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(2);
+ setup_function7();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Servers1, function24)
+ serveSalon(savepoint, "927", "Ann3143A", kEntityAnna, "Ann31444", "112C", kAction122288808, "928", &ENTITY_PARAM(1, 1));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Servers1, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityServers1);
+
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Servers1, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param2) {
+ UPDATE_PARAM_PROC(params->param2, getState()->time, 900)
+ ENTITY_PARAM(1, 5) = 1;
+ params->param1 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+ if (!getEntities()->isInKitchen(kEntityServers1) || !getEntities()->isSomebodyInsideRestaurantOrSalon())
+ break;
+
+ if (ENTITY_PARAM(1, 5)) {
+ setCallback(2);
+ setup_function28();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 4)) {
+ setCallback(3);
+ setup_function29();
+ break;
+ }
+
+ if (ENTITY_PARAM(1, 2)) {
+ setCallback(4);
+ setup_function7();
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ params->param1 = 1;
+ break;
+
+ case kAction201431954:
+ ENTITY_PARAM(1, 2) = 0;
+ ENTITY_PARAM(1, 3) = 0;
+ ENTITY_PARAM(1, 4) = 0;
+ ENTITY_PARAM(1, 5) = 0;
+
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationOutsideCompartment;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Servers1, function27)
+ serveSalon(savepoint, "929", "", kEntityAugust, "Aug4003", "122D", kAction134486752, "930", &ENTITY_PARAM(1, 3));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Servers1, function28)
+ serveSalon(savepoint, "931", "", kEntityAugust, "Aug4004", "122E", kAction125826561, "930", &ENTITY_PARAM(1, 5));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Servers1, function29)
+ serveSalon(savepoint, "932", "", kEntityAnna, "Ann4151", "127D", kAction122288808, "930", &ENTITY_PARAM(1, 4));
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Servers1, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityServers1);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Servers1, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(32, Servers1)
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, Position position, bool shouldUpdatePosition, uint *parameter2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ if (shouldUpdatePosition) {
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ }
+
+ setCallback(1);
+ setup_draw(seq1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (position)
+ getEntities()->updatePositionEnter(kEntityServers1, kCarRestaurant, position);
+
+ getSavePoints()->push(kEntityServers1, entity, kAction136455232);
+
+ setCallback(2);
+ setup_callSavepoint(seq2, entity, kActionDrawTablesWithChairs, seq3);
+ break;
+
+ case 2:
+ if (position)
+ getEntities()->updatePositionExit(kEntityServers1, kCarRestaurant, position);
+
+ setCallback(3);
+ setup_draw(seq4);
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_5900;
+ getEntities()->clearSequences(kEntityServers1);
+ *parameter = 0;
+
+ if (parameter2 != NULL)
+ *parameter2 = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Servers1::serveSalon(const SavePoint &savepoint, const char *seq1, const char *snd1, EntityIndex entity, const char *snd2, const char *seq2, ActionIndex action, const char *seq3, uint *parameter) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_draw("816DD");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceRight(kEntityServers1, seq1);
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ if (!strcmp(snd1, ""))
+ getSound()->playSound(kEntityServers1, snd1);
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityServers1, entity, kAction122358304);
+
+ getSound()->playSound(kEntityServers1, snd2);
+
+ setCallback(3);
+ setup_updatePosition(seq2, kCarRestaurant, 57);
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityServers1, entity, action);
+
+ setCallback(4);
+ setup_draw(seq3);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityServers1, "816UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityServers1);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityServers1);
+ getData()->entityPosition = kPosition_5900;
+ *parameter = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/servers1.h b/engines/lastexpress/entities/servers1.h
new file mode 100644
index 0000000000..14a6bf09a6
--- /dev/null
+++ b/engines/lastexpress/entities/servers1.h
@@ -0,0 +1,167 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SERVERS1_H
+#define LASTEXPRESS_SERVERS1_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Servers1 : public Entity {
+public:
+ Servers1(LastExpressEngine *engine);
+ ~Servers1() {}
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence, CarIndex car, Position position)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(function10)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION(function13)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION(function16)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function19)
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+
+private:
+ void serveTable(const SavePoint &savepoint, const char *seq1, EntityIndex entity, const char *seq2, const char *seq3, const char *seq4, uint *parameter, Position position = 0, bool updatePosition = true, uint *parameter2 = NULL);
+ void serveSalon(const SavePoint &savepoint, const char *seq1, const char *snd1, EntityIndex entity, const char *snd2, const char *seq2, ActionIndex action, const char *seq3, uint *parameter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SERVERS1_H
diff --git a/engines/lastexpress/entities/sophie.cpp b/engines/lastexpress/entities/sophie.cpp
new file mode 100644
index 0000000000..1b2d7b92ee
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.cpp
@@ -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$
+ *
+ */
+
+#include "lastexpress/entities/sophie.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+#define CHAPTER_IMPLEMENTATION() \
+ switch (savepoint.action) { \
+ default: \
+ break; \
+ case kActionNone: \
+ setup_chaptersHandler(); \
+ break; \
+ case kActionDefault: \
+ getEntities()->clearSequences(kEntitySophie); \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getData()->clothes = kClothesDefault; \
+ getData()->inventoryItem = kItemNone; \
+ break; \
+ }
+
+#define DEFAULT_ACTION_IMPLEMENTATION() \
+ if (savepoint.action == kActionDefault) { \
+ getData()->entityPosition = kPosition_4840; \
+ getData()->location = kLocationInsideCompartment; \
+ getData()->car = kCarRedSleeping; \
+ getEntities()->clearSequences(kEntitySophie); \
+ }
+
+Sophie::Sophie(LastExpressEngine *engine) : Entity(engine, kEntitySophie) {
+ ADD_CALLBACK_FUNCTION(Sophie, reset);
+ ADD_CALLBACK_FUNCTION(Sophie, updateEntity);
+ ADD_CALLBACK_FUNCTION(Sophie, chaptersHandler);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter1);
+ ADD_CALLBACK_FUNCTION(Sophie, function5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter2);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter3);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter4);
+ ADD_CALLBACK_FUNCTION(Sophie, function9);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5);
+ ADD_CALLBACK_FUNCTION(Sophie, chapter5Handler);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Sophie, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(2, Sophie, updateEntity, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone: {
+ params->param3 = 0;
+
+ // Sophie
+ byte direction = getData()->direction;
+ EntityPosition position = getData()->entityPosition;
+ CarIndex car = getData()->car;
+
+ // Rebecca
+ EntityPosition rebecca_position = getEntityData(kEntityRebecca)->entityPosition;
+ CarIndex rebeccaCar = getEntityData(kEntityRebecca)->car;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntitySophie, kEntityRebecca, 500)
+ || (direction == kDirectionUp && car >= rebeccaCar && position > rebecca_position)
+ || (direction == kDirectionDown && car <= rebeccaCar && position < rebecca_position)) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+
+ break;
+ }
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntitySophie);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntitySophie, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Sophie, chaptersHandler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityRebecca)->entityPosition;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntitySophie);
+ break;
+
+ case 4:
+ getEntities()->drawSequenceLeft(kEntitySophie, "BLANK");
+ break;
+ }
+ break;
+
+ case kAction125242096:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition - 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case kAction136654208:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+
+ case kAction259921280:
+ getData()->entityPosition = (EntityPosition)(getEntityData(kEntityRebecca)->entityPosition + 100);
+ getData()->location = getEntityData(kEntityRebecca)->location;
+ getData()->car = getEntityData(kEntityRebecca)->car;
+
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9460);
+ break;
+
+ case kAction292775040:
+ getData()->entityPosition = kPosition_9270;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarKronos;
+
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_4840);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Sophie, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chaptersHandler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Sophie, function5)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Sophie, chapter2)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Sophie, chapter3)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Sophie, chapter4)
+ CHAPTER_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Sophie, function9)
+ DEFAULT_ACTION_IMPLEMENTATION()
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Sophie, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntitySophie);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Sophie, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_nullfunction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(12, Sophie)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/sophie.h b/engines/lastexpress/entities/sophie.h
new file mode 100644
index 0000000000..24a70188da
--- /dev/null
+++ b/engines/lastexpress/entities/sophie.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SOPHIE_H
+#define LASTEXPRESS_SOPHIE_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Sophie : public Entity {
+public:
+ Sophie(LastExpressEngine *engine);
+ ~Sophie() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Handle chapters events
+ */
+ DECLARE_FUNCTION(chaptersHandler)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function5)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function9)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOPHIE_H
diff --git a/engines/lastexpress/entities/tables.cpp b/engines/lastexpress/entities/tables.cpp
new file mode 100644
index 0000000000..eca60a536b
--- /dev/null
+++ b/engines/lastexpress/entities/tables.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$
+ *
+ */
+
+#include "lastexpress/entities/tables.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tables::Tables(LastExpressEngine *engine, EntityIndex id) : Entity(engine, id) {
+ _id = id;
+
+ ADD_CALLBACK_FUNCTION(Tables, chapter1);
+ ADD_CALLBACK_FUNCTION(Tables, chapter2);
+ ADD_CALLBACK_FUNCTION(Tables, chapter3);
+ ADD_CALLBACK_FUNCTION(Tables, chapter4);
+ ADD_CALLBACK_FUNCTION(Tables, chapter5);
+ ADD_CALLBACK_FUNCTION(Tables, draw);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tables, chapter1)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Tables, chapter2)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Tables, chapter3)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Tables, chapter4)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2)
+ getSound()->playSoundWithSubtitles("LOOP8A.SND", SoundManager::kFlagLoop, kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Tables, chapter5)
+ if (savepoint.action == kActionDefault) {
+ if (_id == kEntityTables2 && getSound()->isBuffered(kEntityTables2))
+ getSound()->processEntry(kEntityTables2);
+
+ setup_draw();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Tables, draw)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Only applicable to Tables2 entity
+ if (_id != kEntityTables2)
+ break;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ if (getState()->time > kTime1165500 && !params->param1) {
+ params->param1 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter3:
+ if (getState()->time > kTime2052000 && !params->param2) {
+ params->param2 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ case kChapter4:
+ if (getState()->time > kTime2488500 && !params->param3) {
+ params->param3 = 1;
+ getSound()->processEntry(kEntityTables2);
+ }
+ break;
+
+ }
+ break;
+
+ case kActionDefault:
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getData()->entityPosition = kPosition_3970;
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getData()->entityPosition = kPosition_4690;
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getData()->entityPosition = kPosition_5420;
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+
+ break;
+
+ case kActionDrawTablesWithChairs:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ getEntities()->drawSequenceLeft(_id, savepoint.param.charValue);
+ } else {
+ switch(_id) {
+ default:
+ break;
+
+ case kEntityTables0:
+ getEntities()->drawSequenceLeft(_id, "001P");
+ break;
+
+ case kEntityTables1:
+ getEntities()->drawSequenceLeft(_id, "005J");
+ break;
+
+ case kEntityTables2:
+ getEntities()->drawSequenceLeft(_id, "009G");
+ break;
+
+ case kEntityTables3:
+ getEntities()->drawSequenceLeft(_id, "010M");
+ break;
+
+ case kEntityTables4:
+ getEntities()->drawSequenceLeft(_id, "014F");
+ break;
+
+ case kEntityTables5:
+ getEntities()->drawSequenceLeft(_id, "024D");
+ break;
+ }
+ }
+ break;
+
+ case kAction136455232:
+ getEntities()->drawSequenceLeft(_id, "BLANK");
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tables.h b/engines/lastexpress/entities/tables.h
new file mode 100644
index 0000000000..e5fe28128b
--- /dev/null
+++ b/engines/lastexpress/entities/tables.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 LASTEXPRESS_TABLES_H
+#define LASTEXPRESS_TABLES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tables : public Entity {
+public:
+ Tables(LastExpressEngine *engine, EntityIndex id);
+ ~Tables() {}
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Draws tables
+ */
+ DECLARE_FUNCTION(draw)
+
+private:
+ EntityIndex _id; ///< Table entity id
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TABLES_H
diff --git a/engines/lastexpress/entities/tatiana.cpp b/engines/lastexpress/entities/tatiana.cpp
new file mode 100644
index 0000000000..8e6be41ad4
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.cpp
@@ -0,0 +1,2272 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/tatiana.h"
+
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Tatiana::Tatiana(LastExpressEngine *engine) : Entity(engine, kEntityTatiana) {
+ ADD_CALLBACK_FUNCTION(Tatiana, reset);
+ ADD_CALLBACK_FUNCTION(Tatiana, playSound);
+ ADD_CALLBACK_FUNCTION(Tatiana, draw);
+ ADD_CALLBACK_FUNCTION(Tatiana, updatePosition);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Tatiana, enterExitCompartment2);
+ ADD_CALLBACK_FUNCTION(Tatiana, callSavepoint);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTicks);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Tatiana, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Tatiana, savegame);
+ ADD_CALLBACK_FUNCTION(Tatiana, updateEntity);
+ ADD_CALLBACK_FUNCTION(Tatiana, function14);
+ ADD_CALLBACK_FUNCTION(Tatiana, function15);
+ ADD_CALLBACK_FUNCTION(Tatiana, function16);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1);
+ ADD_CALLBACK_FUNCTION(Tatiana, function18);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function20);
+ ADD_CALLBACK_FUNCTION(Tatiana, function21);
+ ADD_CALLBACK_FUNCTION(Tatiana, function22);
+ ADD_CALLBACK_FUNCTION(Tatiana, function23);
+ ADD_CALLBACK_FUNCTION(Tatiana, function24);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function27);
+ ADD_CALLBACK_FUNCTION(Tatiana, function28);
+ ADD_CALLBACK_FUNCTION(Tatiana, function29);
+ ADD_CALLBACK_FUNCTION(Tatiana, function30);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function33);
+ ADD_CALLBACK_FUNCTION(Tatiana, function34);
+ ADD_CALLBACK_FUNCTION(Tatiana, function35);
+ ADD_CALLBACK_FUNCTION(Tatiana, function36);
+ ADD_CALLBACK_FUNCTION(Tatiana, function37);
+ ADD_CALLBACK_FUNCTION(Tatiana, function38);
+ ADD_CALLBACK_FUNCTION(Tatiana, function39);
+ ADD_CALLBACK_FUNCTION(Tatiana, function40);
+ ADD_CALLBACK_FUNCTION(Tatiana, function41);
+ ADD_CALLBACK_FUNCTION(Tatiana, function42);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function45);
+ ADD_CALLBACK_FUNCTION(Tatiana, function46);
+ ADD_CALLBACK_FUNCTION(Tatiana, function47);
+ ADD_CALLBACK_FUNCTION(Tatiana, function48);
+ ADD_CALLBACK_FUNCTION(Tatiana, function49);
+ ADD_CALLBACK_FUNCTION(Tatiana, function50);
+ ADD_CALLBACK_FUNCTION(Tatiana, function51);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5);
+ ADD_CALLBACK_FUNCTION(Tatiana, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Tatiana, function54);
+ ADD_CALLBACK_FUNCTION(Tatiana, function55);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Tatiana, reset)
+ Entity::reset(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Tatiana, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Tatiana, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SII(4, Tatiana, updatePosition, CarIndex, Position)
+ Entity::updatePosition(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(5, Tatiana, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(6, Tatiana, enterExitCompartment2, ObjectIndex)
+ Entity::enterExitCompartment(savepoint, kPosition_7500, kPosition_7850, kCarRedSleeping, kObjectCompartmentB);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SIIS(7, Tatiana, callSavepoint, EntityIndex, ActionIndex)
+ Entity::callSavepoint(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Tatiana, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(9, Tatiana, updateFromTicks)
+ Entity::updateFromTicks(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(10, Tatiana, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Tatiana, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(12, Tatiana, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(13, Tatiana, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure)) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1010" : "CAT1010A");
+ } else {
+ getSound()->excuseMeCath();
+ }
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Tatiana, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction326348944);
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kAction69239528:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Db" : "673Db", kObjectCompartmentB);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Tatiana, function15)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getProgress().chapter == kChapter1 ? 1 : 2);
+ setup_enterExitCompartment2(getProgress().chapter == kChapter1 ? "603Bb" : "673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1 || getCallback() == 2) {
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction292048641);
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, getProgress().chapter == kChapter1 ? "603Fb" : "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ }
+ break;
+
+ case kAction69239528:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(16, Tatiana, function16, uint32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 < getState()->time && !params->param4) {
+ params->param4 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param2) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ params->param2 = 0;
+ params->param3 = 1;
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param2) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.param.intValue == 49) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1512" : getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ } else {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (params->param2 || params->param3) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "TAT1133A" : "TAT1133B");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param2 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param2 = 0;
+ params->param3 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Tatiana, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityTatiana, kAction191198209, 0);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5419;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Tatiana, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+
+ if (getState()->time > kTime1143000 && !params->param2) {
+ params->param2 = 1;
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ }
+
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->time, 4500)
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (getData()->entityPosition <= kPosition_2330) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction157159392);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionExitCompartment:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction188784532);
+
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ params->param1 = 1;
+ } else {
+ getEntities()->clearSequences(kEntityTatiana);
+ }
+ break;
+
+ case kActionDrawScene:
+ if (!params->param1 && getEntities()->isInSalon(kEntityPlayer)) {
+ getEntities()->drawSequenceRight(kEntityTatiana, "806DS");
+ getEntities()->updateFrame(kEntityTatiana);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Tatiana, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getSound()->isBuffered(kEntityTatiana) || !params->param4 || params->param3 == 2 || getSound()->isBuffered("TAT1066"))
+ goto label_tatiana_chapter1_2;
+
+ UPDATE_PARAM_PROC(params->param5, getState()->timeTicks, 450)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param5 = 0;
+ UPDATE_PARAM_PROC_END
+
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 71)) {
+ UPDATE_PARAM_PROC(params->param6, getState()->timeTicks, 75)
+ getSound()->playSound(kEntityTatiana, params->param3 ? "TAT1069B" : "TAT1069A");
+ getProgress().field_64 = 1;
+ params->param3++;
+ params->param6 = 0;
+ UPDATE_PARAM_PROC_END
+ }
+
+label_tatiana_chapter1_2:
+ TIME_CHECK_SAVEPOINT(kTime1084500, params->param7, kEntityTatiana, kEntityPascale, kAction257489762);
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 90);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ } else {
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityTables4, kAction136455232);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kActionDrawScene:
+ params->param1 = getEntities()->isPlayerPosition(kCarRestaurant, 67) ? 1 : 0;
+ params->param4 = getEntities()->isPlayerPosition(kCarRestaurant, 69)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 70)
+ || getEntities()->isPlayerPosition(kCarRestaurant, 71);
+ break;
+
+ case kAction122288808:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "014A");
+ break;
+
+ case kAction122358304:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+
+ case kAction124973510:
+ setup_function20();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Tatiana, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction223183000);
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 67);
+ getSound()->playSound(kEntityTatiana, "TAT1070");
+
+ setCallback(2);
+ setup_callSavepoint("014C", kEntityTables4, kActionDrawTablesWithChairs, "014D");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 67);
+ getSavePoints()->push(kEntityTatiana, kEntityServers0, kAction188893625);
+
+ setCallback(3);
+ setup_function18();
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAugust, kAction268620864);
+ setup_function21();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Tatiana, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes1;
+
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_8513);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->clothes = kClothesDefault;
+
+ getSound()->playSound(kEntityTatiana, "TAT1071");
+ getEntities()->drawSequenceRight(kEntityTatiana, "604Aa");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentA, true);
+ }
+
+ setCallback(2);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 2:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentA);
+
+ getData()->location = kLocationInsideCompartment;
+
+ getEntities()->clearSequences(kEntityTatiana);
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction135854208);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ // Fallback to next case
+
+ case 3:
+ if (getSound()->isBuffered(kEntityTatiana)) {
+ setCallback(3);
+ setup_updateFromTime(75);
+ } else {
+ setCallback(4);
+ setup_playSound("TAT1071A");
+ }
+ break;
+
+ case 4:
+ getData()->entityPosition = kPosition_7500;
+
+ getSavePoints()->push(kEntityTatiana, kEntityVassili, kAction168459827);
+
+ setCallback(5);
+ setup_function16(kTime1156500);
+ break;
+
+ case 5:
+ case 6:
+ if (getProgress().field_14 == 29) {
+ setCallback(6);
+ setup_function16((uint)getState()->time + 900);
+ } else {
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ setup_function22();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Tatiana, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 == kTimeInvalid || getState()->time <= kTime1179000)
+ goto label_update;
+
+ UPDATE_PARAM_PROC_TIME(kTime1233000, ((!getEvent(kEventTatianaAskMatchSpeakRussian) && !getEvent(kEventTatianaAskMatch)) || getEntities()->isInGreenCarEntrance(kEntityPlayer)), params->param1, 0)
+label_update:
+ if (!getEvent(kEventTatianaAskMatchSpeakRussian)
+ && !getEvent(kEventTatianaAskMatch)
+ && getInventory()->hasItem(kItemMatchBox)
+ && getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getObjects()->update(kObject25, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectTrainTimeTable, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorForward);
+ }
+ UPDATE_PARAM_PROC_END
+
+ params->param1 = kTimeInvalid;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 71);
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getData()->inventoryItem = kItemNone;
+
+ setup_function23();
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaGivePoem);
+ break;
+
+ case kActionOpenDoor:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaAskMatchSpeakRussian);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityVassili, kAction122732000);
+
+ setCallback(1);
+ setup_function15();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306B");
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarGreenSleeping, 71);
+ break;
+
+ case 3:
+ getAction()->playAnimation(getEvent(kEventAlexeiSalonVassili) ? kEventTatianaAskMatchSpeakRussian : kEventTatianaAskMatch);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
+ getData()->inventoryItem = kItemParchemin;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+
+ case 4:
+ getAction()->playAnimation(kEventTatianaGivePoem);
+ getInventory()->removeItem(kItemParchemin);
+ getScenes()->processScene();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Tatiana, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setup_function24();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Tatiana, function24)
+ if (savepoint.action == kActionDefault) {
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObject25, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getObjects()->update(kObjectTrainTimeTable, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 70);
+ getEntities()->updatePositionExit(kEntityTatiana, kCarGreenSleeping, 71);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Tatiana, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject41, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+
+ getData()->entityPosition = kPosition_5420;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Tatiana, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime1800000 && params->param1 && getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+ setup_function28();
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "024A");
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kAction136455232);
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 64) || getEntities()->isPlayerPosition(kCarRestaurant, 65)) {
+ getData()->inventoryItem = kItemNone;
+ setup_function27();
+ }
+ break;
+
+ case kAction290869168:
+ params->param1 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Tatiana, function27)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(getEvent(kEventTatianaGivePoem) ? 1 : 2);
+ setup_savegame(kSavegameTypeEvent, getEvent(kEventTatianaGivePoem) ? kEventTatianaBreakfastAlexei : kEventTatianaBreakfast);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfastAlexei);
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->setLocationAndProcess(kItem11, kObjectLocation1);
+ setup_function28();
+ break;
+
+ case 2:
+ RESET_ENTITY_STATE(kEntityAlexei, Alexei, setup_function30);
+ getAction()->playAnimation(kEventTatianaBreakfast);
+ if (getInventory()->hasItem(kItemParchemin)) {
+ getAction()->playAnimation(kEventTatianaBreakfastGivePoem);
+ getInventory()->removeItem(kItemParchemin);
+ } else {
+ getAction()->playAnimation(kEventTatianaAlexei);
+ }
+ setup_function28();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Tatiana, function28)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->inventoryItem = kItemNone;
+ getData()->location = kLocationOutsideCompartment;
+
+ getSavePoints()->push(kEntityTatiana, kEntityTables5, kActionDrawTablesWithChairs, "024D");
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction236053296, (getEvent(kEventTatianaBreakfastAlexei) || getEvent(kEventTatianaBreakfast)) ? 69 : 0);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function29();
+ break;
+
+ case kAction123857088:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "018G");
+
+ setCallback(1);
+ setup_updateFromTime(1800);
+ break;
+
+ case kAction156444784:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "BLANK");
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Tatiana, function29)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getEntities()->updatePositionEnter(kEntityTatiana, kCarRestaurant, 63);
+
+ setCallback(2);
+ setup_callSavepoint("018H", kEntityTables1, kActionDrawTablesWithChairs, "018A");
+ break;
+
+ case 2:
+ getEntities()->updatePositionExit(kEntityTatiana, kCarRestaurant, 63);
+ getSavePoints()->push(kEntityTatiana, kEntityServers1, kAction302203328);
+ getEntities()->drawSequenceRight(kEntityTatiana, "805DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityTatiana);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setup_function30();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Tatiana, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_function14();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function16(kTimeEnd);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Tatiana, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_1750;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ // Update inventory
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+
+ if (getEvent(kEventTatianaBreakfastGivePoem) || (getEvent(kEventTatianaGivePoem) && !getEvent(kEventTatianaBreakfastAlexei)))
+ getInventory()->get(kItemParchemin)->location = kObjectLocation2;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Tatiana, chapter3Handler)
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+ EntityData::EntityParametersSIII *parameters1 = (EntityData::EntityParametersSIII*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!parameters->param2 && !parameters->param5) {
+ parameters->param1 -= getState()->timeDelta;
+
+ if (getState()->timeDelta > parameters->param1) {
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, (char *)&parameters1->seq);
+ getSound()->playSound(kEntityTatiana, (char *)&parameters->seq);
+
+ if (parameters->param3 == 4 && getEntities()->isInSalon(kEntityPlayer))
+ getProgress().field_90 = 1;
+
+ parameters->param2 = 1;
+ }
+ }
+
+ if (parameters->param4 && parameters->param5) {
+ UPDATE_PARAM_CHECK(parameters->param4, getState()->time, 6300)
+ if (getEntities()->isSomebodyInsideRestaurantOrSalon()) {
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updatePosition("110E", kCarRestaurant, 52);
+ }
+ }
+ }
+ break;
+
+ case kActionEndSound:
+ parameters->param2 = 0;
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ parameters->param5 = 1;
+ break;
+
+ case 1:
+ parameters->param1 = 900;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110A");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ strcpy((char *)&parameters1->seq, "110A");
+ break;
+
+ case 2:
+ parameters->param1 = 9000;
+ strcpy((char *)&parameters->seq, "Tat3160C");
+ strcpy((char *)&parameters1->seq, "110C");
+ break;
+
+ case 3:
+ parameters->param1 = 13500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160D");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 4:
+ parameters->param1 = 9000;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160E");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 5:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160G");
+ strcpy((char *)&parameters1->seq, "110D");
+ break;
+
+ case 6:
+ parameters->param1 = 4500;
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110B");
+ strcpy((char *)&parameters->seq, "Tat3160B");
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122358304);
+ getSavePoints()->push(kEntityTatiana, kEntityKronos, kAction157159392);
+ getEntities()->drawSequenceLeft(kEntityTatiana, "110C");
+ getSound()->playSound(kEntityTatiana, "Tat3160A");
+
+ parameters->param2 = 1;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction122288808);
+ setup_function33();
+ }
+ break;
+
+ case kAction101169422:
+ parameters->param4 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Tatiana, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ setCallback(1);
+ setup_updateFromTime(75);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function14();
+ break;
+
+ case 3:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Tatiana, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2097000);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation1;
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850))
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ setup_function15();
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_updateEntity(kCarKronos, kPosition_9270);
+ break;
+
+ case 3:
+ setup_function35();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Tatiana, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1
+ && getInventory()->hasItem(kItemFirebird)
+ && getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && (getState()->time < kTime2133000 || getProgress().field_40)) {
+ setCallback(1);
+ setup_function41();
+ break;
+ }
+
+label_callback_1:
+ if (getState()->time > kTime2133000) {
+ if (getData()->car >= kCarRedSleeping || (getData()->car == kCarGreenSleeping && getData()->entityPosition > kPosition_5790))
+ setup_function36();
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->car = kCarKronos;
+ getData()->entityPosition = kPosition_6000;
+ getData()->location = kLocationInsideCompartment;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ params->param1 = 1;
+ goto label_callback_1;
+ }
+ break;
+
+ case kAction191668032:
+ setup_function36();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Tatiana, function36)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ if (!getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850) || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ setCallback(2);
+ setup_function14();
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ break;
+
+ case 2:
+ setup_function37();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Tatiana, function37)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getInventory()->get(kItemFirebird)->location != kObjectLocation1 && getInventory()->get(kItemFirebird)->location != kObjectLocation2) {
+ if(!params->param3)
+ params->param3 = (uint)getState()->time + 900;
+
+ if (params->param4 != kTimeInvalid && params->param3 < getState()->time) {
+ UPDATE_PARAM_PROC_TIME(kTime2227500, !getEntities()->isPlayerInCar(kCarRedSleeping), params->param4, 450)
+ getProgress().field_5C = 1;
+ if (getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070)) {
+ setup_function38();
+ break;
+ }
+ UPDATE_PARAM_PROC_END
+ }
+ }
+
+ if (params->param1) {
+ UPDATE_PARAM(params->param5, getState()->timeTicks, 75);
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ params->param1 = 0;
+ params->param2 = 1;
+ }
+
+ params->param5 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (params->param1) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ if (savepoint.param.intValue == 49) {
+ setCallback(4);
+ setup_playSound(getSound()->justAMinuteCath());
+ break;
+ }
+
+ if (getInventory()->hasItem(kItemPassengerList)) {
+ setCallback(5);
+ setup_playSound(rnd(2) ? "CAT1512" : getSound()->wrongDoorCath());
+ break;
+ }
+
+ setCallback(6);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ if (savepoint.param.intValue == 49) {
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+ break;
+
+ case kActionDrawScene:
+ if (params->param1 || params->param2) {
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorNormal, kCursorHand);
+
+ params->param1 = 0;
+ params->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound(rnd(2) ? "TAT1133A" : "TAT1133B");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentB, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ getObjects()->update(kObject49, kEntityTatiana, kObjectLocation1, kCursorTalk, kCursorNormal);
+ params->param1 = 1;
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ params->param1 = 0;
+ params->param2 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Tatiana, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 450);
+
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(4);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionDefault:
+ getData()->clothes = kClothes3;
+
+ setCallback(1);
+ setup_enterExitCompartment("673Jb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(2);
+ setup_function42(kCarRedSleeping, kPosition_4070);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Gf");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentF, true);
+
+ setCallback(3);
+ setup_playSound("Tat3164");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236241630);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+
+ case 6:
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentF, true);
+ getEntities()->clearSequences(kEntityTatiana);
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(7);
+ setup_playSound("ANN3011");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_updateFromTime(900);
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_enterExitCompartment("673Jf", kObjectCompartmentF);
+ break;
+
+ case 9:
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(10);
+ setup_function42(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case 10:
+ getSavePoints()->push(kEntityTatiana, kEntityAnna, kAction236517970);
+
+ setCallback(11);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function39();
+ break;
+ }
+ break;
+
+ case kAction100906246:
+ setCallback(6);
+ setup_enterExitCompartment("673Df", kObjectCompartmentF);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Tatiana, function39)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1 && getEntities()->isDistanceBetweenEntities(kEntityTatiana, kEntityPlayer, 1000)) {
+ params->param1 = 1;
+ getSound()->playSound(kEntityTatiana, "Tat3164"); // Tatiana weeping
+ }
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Tatiana, function40)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarKronos)
+ || getData()->car != getEntityData(kEntityPlayer)->car
+ || getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMe:
+ if (getEvent(kEventTatianaAskMatchSpeakRussian) || getEvent(kEventTatianaAskMatch) || getEvent(kEventVassiliSeizure))
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT1001A" : "CAT1010");
+ else
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityTatiana, kCarKronos, kPosition_9270))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Tatiana, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1)
+ break;
+
+ if (getEntities()->checkFields19(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && !getEvent(kEventVassiliCompartmentStealEgg)
+ && (getState()->time <= kTime2133000 || getProgress().field_40)) {
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7500)) {
+
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction235061888);
+ getEntities()->clearSequences(kEntityTatiana);
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ getData()->location = kLocationInsideCompartment;
+
+ if (getInventory()->hasItem(kItemFirebird)) {
+ getAction()->playAnimation(kEventTatianaCompartmentStealEgg);
+ getInventory()->removeItem(kItemFirebird);
+ getInventory()->get(kItemFirebird)->location = kObjectLocation2;
+ } else {
+ getAction()->playAnimation(kEventTatianaCompartment);
+ }
+
+ getScenes()->loadSceneFromObject(kObjectCompartmentB);
+
+ setCallback(4);
+ setup_updateFromTime(150);
+ }
+ } else {
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartmentB, true);
+
+ if (getState()->time < kTime2133000 || getProgress().field_40) {
+ setCallback(3);
+ setup_function40();
+ break;
+ }
+
+ getEntities()->clearSequences(kEntityTatiana);
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationOutsideCompartment;
+
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function51);
+
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartmentB, true);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_playSound("Tat3161B");
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityTatiana, kEntityCoudert, kAction168316032);
+ params->param1 = 1;
+ break;
+
+ case 3:
+ case 6:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ CALLBACK_ACTION();
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15();
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function40();
+ break;
+ }
+ break;
+
+ case kAction154071333:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(42, Tatiana, function42, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath || savepoint.action == kActionExcuseMe) {
+ getSound()->playSound(kEntityPlayer, "Tat3124", getSound()->getSoundFlag(kEntityTatiana));
+ return;
+ }
+
+ Entity::updateEntity(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(43, Tatiana, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothes2;
+ getData()->inventoryItem = kItemNone;
+
+ ENTITY_PARAM(0, 1) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(44, Tatiana, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function16(kTime2362500);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function45();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(45, Tatiana, function45)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+
+ case 2:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction123712592);
+ setup_function46();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(46, Tatiana, function46)
+ // Expose parameters as IIIIIS and ignore the default exposed parameters
+ EntityData::EntityParametersI5S *parameters = (EntityData::EntityParametersI5S*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!parameters->param2 && !parameters->param3) {
+ parameters->param1 -= getState()->timeDelta;
+
+ if (parameters->param1 < getState()->timeDelta) {
+ getSound()->playSound(kEntityTatiana, (char *)&parameters->seq);
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityTatiana, kEntityPlayer, 2000)) {
+ if (parameters->param4 == 4)
+ getProgress().field_8C = 1;
+ else if (parameters->param4 == 7)
+ getProgress().field_88 = 1;
+ }
+
+ parameters->param2 = 1;
+ }
+ }
+
+ if (CURRENT_PARAM(1, 1) == kTimeInvalid || getState()->time <= kTime2394000)
+ break;
+
+ if (getState()->time >= kTime2398500) {
+ CURRENT_PARAM(1, 1) = kTimeInvalid;
+ } else {
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) || !CURRENT_PARAM(1, 1))
+ CURRENT_PARAM(1, 1) = (uint)getState()->time;
+
+ if (CURRENT_PARAM(1, 1) >= getState()->time)
+ break;
+
+ CURRENT_PARAM(1, 1) = kTimeInvalid;
+ }
+
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->excuseMe(kEntityTatiana);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62))
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 72);
+ }
+
+ getSavePoints()->push(kEntityTatiana, kEntityAlexei, kAction123536024);
+
+ setup_function47();
+ break;
+
+ case kActionEndSound:
+ parameters->param2 = 0;
+ ++parameters->param4;
+
+ switch(parameters->param4) {
+ default:
+ parameters->param1 = 162000;
+ break;
+
+ case 1:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165F");
+ break;
+
+ case 2:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165B");
+ break;
+
+ case 3:
+ parameters->param1 = 1800;
+ strcpy((char *)&parameters->seq, "Tat4165G");
+ break;
+
+ case 4:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165H");
+ break;
+
+ case 5:
+ parameters->param1 = 2700;
+ strcpy((char *)&parameters->seq, "Tat4165C");
+ break;
+
+ case 6:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165D");
+ break;
+
+ case 7:
+ parameters->param1 = 900;
+ strcpy((char *)&parameters->seq, "Tat4165E");
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306E");
+ parameters->param1 = 450;
+ strcpy((char *)&parameters->seq, "Tat4165A");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer)) {
+ parameters->param3 = 1;
+
+ if (parameters->param2) {
+ getSound()->removeFromQueue(kEntityTatiana);
+ getSavePoints()->call(kEntityTatiana, kEntityTatiana, kActionEndSound);
+ }
+ } else {
+ parameters->param3 = 0;
+ parameters->param5 = 0;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 62) && !parameters->param5) {
+ setCallback(1);
+ setup_draw("306D");
+ }
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityTatiana, "306E");
+ parameters->param5 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(47, Tatiana, function47)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 2:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setCallback(3);
+ setup_function16(kTime2407500);
+ break;
+
+ case 3:
+ case 4:
+ if (ENTITY_PARAM(0, 1) && getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1) {
+ setup_function48();
+ } else {
+ setCallback(4);
+ setup_function16(900);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(48, Tatiana, function48)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ if (!getEvent(kEventTatianaTylerCompartment) && getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
+ params->param1 = 1;
+ getProgress().field_E4 = 1;
+ getObjects()->update(kObjectCompartment1, kEntityTatiana, getObjects()->get(kObjectCompartment1).location, kCursorNormal, kCursorHand);
+ }
+
+ if (!params->param1)
+ goto label_end;
+ }
+
+ if (!getEntities()->checkFields19(kEntityPlayer, kCarGreenSleeping, kPosition_7850)) {
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ params->param1 = 0;
+ }
+
+ if (!params->param1 || getSound()->isBuffered(kEntityTatiana))
+ goto label_end;
+
+ UPDATE_PARAM_GOTO(params->param2, getState()->timeTicks, 5 * (3 * rnd(5) + 30), label_end);
+
+ getSound()->playSound(kEntityTatiana, "LIB012", SoundManager::kFlagDefault);
+ params->param2 = 0;
+
+label_end:
+ if (getEvent(kEventTatianaTylerCompartment) || getState()->time > kTime2475000) {
+ if (params->param1)
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+
+ getProgress().field_E4 = 0;
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartment2, true);
+
+ setCallback(3);
+ setup_updateEntity(kCarRedSleeping, kPosition_7500);
+ }
+ break;
+
+ case kActionOpenDoor:
+ params->param1 = 0;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaTylerCompartment);
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("673Bb", kObjectCompartmentB);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 2:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartment2, true);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_enterExitCompartment2("673Db", kObjectCompartmentB);
+ break;
+
+ case 4:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityTatiana);
+
+ setup_function49();
+ break;
+
+ case 5:
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ getAction()->playAnimation(kEventTatianaTylerCompartment);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getScenes()->loadScene(kScene41);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_updateEntity(kCarGreenSleeping, kPosition_7500);
+ break;
+
+ case 7:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "673Fb");
+ getEntities()->enterCompartment(kEntityTatiana, kObjectCompartment2, true);
+ break;
+ }
+ break;
+
+ case kAction238790488:
+ params->param1 = 0;
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, getObjects()->get(kObjectCompartment1).location, kCursorHandKnock, kCursorHand);
+ getEntities()->exitCompartment(kEntityTatiana, kObjectCompartment2, true);
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_9460;
+
+ setCallback(6);
+ setup_updateFromTime(1800);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(49, Tatiana, function49)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_7500;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction169360385:
+ setup_function50();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(50, Tatiana, function50)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2520000 && !params->param1) {
+ params->param1 = 1;
+ setup_function51();
+ }
+ break;
+
+ case kActionEndSound:
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionKnock:
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSound(kEntityPlayer, "LIB012");
+ break;
+
+ case kActionOpenDoor:
+ getSound()->playSound(kEntityPlayer, "LIB014");
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliDeadAlexei);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentB, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject49, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject48, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartmentA, kEntityTatiana, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ if (!getSound()->isBuffered(kEntityTatiana))
+ getSound()->playSound(kEntityTatiana, "Tat4166");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS013"))
+ getSound()->processEntry("MUS013");
+
+ getAction()->playAnimation(kEventVassiliDeadAlexei);
+ getSavePoints()->push(kEntityTatiana, kEntityAbbot, kAction104060776);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 38);
+
+ setup_function51();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(51, Tatiana, function51)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(52, Tatiana, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(53, Tatiana, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function54();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(54, Tatiana, function54)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param2) {
+ switch (params->param1) {
+ default:
+ break;
+
+ case 0:
+ getSound()->playSound(kEntityTatiana, "Tat5167A");
+ params->param2 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTatiana, "Tat5167B");
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTatiana, "Tat5167C");
+ params->param2 = 1;
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTatiana, "Tat5167D");
+ params->param2 = 1;
+ break;
+ }
+ }
+
+ if (params->param1 > 3) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, 225);
+
+ params->param1 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction1:
+ getData()->inventoryItem = kItemNone;
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventTatianaVassiliTalk);
+ break;
+
+ case kActionEndSound:
+ ++params->param1;
+ params->param2 = 0;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033A");
+ getData()->inventoryItem = kItemInvalid;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ if (getSound()->isBuffered(kEntityTatiana))
+ getSound()->processEntry(kEntityTatiana);
+
+ getAction()->playAnimation(isNight() ? kEventTatianaVassiliTalkNight : kEventTatianaVassiliTalk);
+ getScenes()->processScene();
+
+ params->param1 = 4;
+ params->param2 = 0;
+ params->param3 = 0;
+ }
+ break;
+
+ case kAction203078272:
+ getEntities()->drawSequenceLeft(kEntityTatiana, "033E");
+ break;
+
+ case kAction236060709:
+ getData()->inventoryItem = kItemNone;
+ setup_function55();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(55, Tatiana, function55)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityTatiana);
+ // fall back to next action
+
+ case kActionDrawScene:
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 72))
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 86);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/tatiana.h b/engines/lastexpress/entities/tatiana.h
new file mode 100644
index 0000000000..171f7d62d3
--- /dev/null
+++ b/engines/lastexpress/entities/tatiana.h
@@ -0,0 +1,235 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_TATIANA_H
+#define LASTEXPRESS_TATIANA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Tatiana : public Entity {
+public:
+ Tatiana(LastExpressEngine *engine);
+ ~Tatiana() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the position
+ *
+ * @param sequence1 The sequence to draw
+ * @param car The car
+ * @param position The position
+ */
+ DECLARE_FUNCTION_3(updatePosition, const char *sequence1, CarIndex car, Position position)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Handles entering/exiting a compartment and updates position/play animation
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment2, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Call a savepoint (or draw sequence in default case)
+ *
+ * @param sequence1 The sequence to draw in the default case
+ * @param entity The entity
+ * @param action The action
+ * @param sequence2 The sequence name for the savepoint
+ */
+ DECLARE_FUNCTION_4(callSavepoint, const char *sequence1, EntityIndex entity, ActionIndex action, const char *sequence2)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Updates parameter 2 using ticks value
+ *
+ * @param savepoint The savepoint
+ * - ticks to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTicks)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+ DECLARE_FUNCTION_1(function16, uint32)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(function24)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function27)
+ DECLARE_FUNCTION(function28)
+ DECLARE_FUNCTION(function29)
+ DECLARE_FUNCTION(function30)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+ DECLARE_FUNCTION(function36)
+ DECLARE_FUNCTION(function37)
+ DECLARE_FUNCTION(function38)
+ DECLARE_FUNCTION(function39)
+ DECLARE_FUNCTION(function40)
+ DECLARE_FUNCTION(function41)
+
+ /**
+ * ???
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(function42, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function45)
+ DECLARE_FUNCTION(function46)
+ DECLARE_FUNCTION(function47)
+ DECLARE_FUNCTION(function48)
+ DECLARE_FUNCTION(function49)
+ DECLARE_FUNCTION(function50)
+ DECLARE_FUNCTION(function51)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function54)
+ DECLARE_FUNCTION(function55)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TATIANA_H
diff --git a/engines/lastexpress/entities/train.cpp b/engines/lastexpress/entities/train.cpp
new file mode 100644
index 0000000000..b3aa6e9a66
--- /dev/null
+++ b/engines/lastexpress/entities/train.cpp
@@ -0,0 +1,575 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/train.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Train::Train(LastExpressEngine *engine) : Entity(engine, kEntityTrain) {
+ ADD_CALLBACK_FUNCTION(Train, savegame);
+ ADD_CALLBACK_FUNCTION(Train, chapter1);
+ ADD_CALLBACK_FUNCTION(Train, chapter2);
+ ADD_CALLBACK_FUNCTION(Train, chapter3);
+ ADD_CALLBACK_FUNCTION(Train, chapter4);
+ ADD_CALLBACK_FUNCTION(Train, chapter5);
+ ADD_CALLBACK_FUNCTION(Train, harem);
+ ADD_CALLBACK_FUNCTION(Train, process);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(1, Train, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(2, Train, chapter1)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Train, chapter2)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Train, chapter3)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Train, chapter4)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Train, chapter5)
+ if (savepoint.action == kActionDefault)
+ setup_process();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Train, harem, ObjectIndex, uint32)
+ if (savepoint.action != kActionDefault)
+ return;
+
+ switch (params->param1) {
+ default:
+ error("Train::harem: Invalid value for parameter 1: %d", params->param1);
+ break;
+
+ case kObjectCompartment5:
+ params->param3 = kPosition_4840;
+ break;
+
+ case kObjectCompartment6:
+ params->param3 = kPosition_4070;
+ break;
+
+ case kObjectCompartment7:
+ params->param3 = kPosition_3050;
+ break;
+
+ case kObjectCompartment8:
+ params->param3 = kPosition_2740;
+ break;
+ }
+
+ params->param4 = getEntities()->isInsideCompartment(kEntityAlouan, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param5 = (ENTITY_PARAM(0, 7) - params->param3) < 1 ? true : false;
+ params->param6 = getEntities()->isInsideCompartment(kEntityYasmin, kCarGreenSleeping, (EntityPosition)params->param3);
+ params->param7 = getEntities()->isInsideCompartment(kEntityHadija, kCarGreenSleeping, (EntityPosition)params->param3);
+
+ getObjects()->update((ObjectIndex)params->param1, kEntityTrain, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ // Knock / closed door sound
+ getSound()->playSound(kEntityTables5, (params->param2 == 8) ? "LIB012" : "LIB013", SoundManager::kFlagDefault);
+
+ if (params->param4 && params->param5) {
+
+ ENTITY_PARAM(0, 5)++;
+
+ switch (ENTITY_PARAM(0, 5)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1016", SoundManager::kFlagDefault, 150);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1015A", SoundManager::kFlagDefault, 15);
+ getSound()->playSound(kEntityTables5, "Har1015", SoundManager::kFlagDefault, 150);
+ break;
+ }
+
+ // Update progress
+ getProgress().field_DC = 1;
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+
+ // Done with it!
+ return;
+ }
+
+ if (params->param6 && params->param7) {
+
+ ENTITY_PARAM(0, 6)++;
+
+ switch(ENTITY_PARAM(0, 6)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (!params->param5) {
+
+ if (params->param6) {
+ ENTITY_PARAM(0, 3)++;
+
+ switch(ENTITY_PARAM(0, 3)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1012", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1012A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ handleCompartmentAction();
+ return;
+ } else {
+
+ if (params->param4) {
+ ENTITY_PARAM(0, 1)++;
+
+ if (ENTITY_PARAM(0, 1) <= 1)
+ getSound()->playSound(kEntityTables5, "Har1014", SoundManager::kFlagDefault, 15);
+ else
+ params->param8 = 1;
+
+ getProgress().field_DC = 1;
+
+ handleCompartmentAction();
+ return;
+ }
+
+ if (params->param7) {
+ ENTITY_PARAM(0, 4)++;
+
+ if (ENTITY_PARAM(0, 4) <= 1) {
+ getSound()->playSound(kEntityTables5, "Har1011", SoundManager::kFlagDefault, 15);
+ handleCompartmentAction();
+ return;
+ }
+ }
+ }
+
+ params->param8 = 1;
+ handleCompartmentAction();
+ return;
+ }
+
+ ENTITY_PARAM(0, 2) += 1;
+
+ switch (ENTITY_PARAM(0, 2)) {
+ default:
+ params->param8 = 1;
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityTables5, "Har1013", SoundManager::kFlagDefault, 15);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityTables5, "Har1013A", SoundManager::kFlagDefault, 15);
+ break;
+ }
+
+ getProgress().field_E0 = 1;
+
+ handleCompartmentAction();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Train, process)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ // Play smoke animation
+ if ((getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && params->param4 && !params->param5) {
+
+ params->param4 -= 1;
+
+ if (!params->param4 && getProgress().jacket == kJacketGreen) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+ params->param5 = 1;
+ getScenes()->processScene();
+ }
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM_GOTO(params1->param7, getState()->time, 900, label_process);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 58);
+ }
+
+ params1->param7 = 0;
+
+label_process:
+ if (params->param7) {
+ if (!params1->param8) {
+ params1->param8 = (uint)(getState()->time + 4500);
+
+ if (!params1->param8)
+ params->param7 = 0;
+ }
+
+ if (params1->param8 && params1->param8 < getState()->time) {
+ params->param7 = 0;
+ params1->param8 = 0;
+ }
+ }
+
+ // Update object
+ if (ENTITY_PARAM(0, 8) && !getSound()->isBuffered(kEntityTables5)) {
+ getObjects()->update((ObjectIndex)ENTITY_PARAM(0, 8), getObjects()->get((ObjectIndex)ENTITY_PARAM(0, 8)).entity, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ ENTITY_PARAM(0, 8) = 0;
+ }
+
+ // Play clock sound
+ if (params->param6 && !getSound()->isBuffered("ZFX1001", true))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor: {
+ // Handle opening harem compartments
+ ObjectIndex compartment = (ObjectIndex)savepoint.param.intValue;
+ if (compartment == kObjectCompartment5 || compartment == kObjectCompartment6 || compartment == kObjectCompartment7 || compartment == kObjectCompartment8) {
+ setCallback(savepoint.action == kActionKnock ? 3 : 4);
+ setup_harem(compartment, savepoint.action);
+ }
+ break;
+ }
+
+ case kActionDefault:
+ params->param3 = 1;
+ if (getProgress().chapter < kChapter5) {
+ getObjects()->update(kObjectCompartment5, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment6, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment7, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment8, kEntityTrain, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+ getData()->entityPosition = kPosition_30000;
+ break;
+
+ case kActionDrawScene:
+ getData()->car = getEntityData(kEntityPlayer)->car;
+
+ // Play clock sound
+ if (getEntities()->isPlayerPosition(kCarRestaurant, 81)) {
+ params->param6 = 1;
+ if (!getSound()->isBuffered("ZFX1001"))
+ getSound()->playSound(kEntityPlayer, "ZFX1001");
+ } else {
+ params->param6 = 0;
+ if (getSound()->isBuffered("ZFX1001", true))
+ getSound()->removeFromQueue("ZFX1001");
+ }
+
+ // Draw moving background behind windows
+ if (params->param3) {
+ if (getEntityData(kEntityPlayer)->car != (CarIndex)params->param1 || isNight() != (bool)(params->param2)) {
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kCarBaggageRear:
+ case kCarBaggage:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "B1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "B1WNN" : "B1WND");
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getProgress().isNightTime)
+ getEntities()->drawSequenceLeft(kEntityTrain, "S1WNM");
+ else
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "S1WNN" : "S1WND");
+ break;
+
+ case kCarRestaurant:
+ getEntities()->drawSequenceLeft(kEntityTrain, isNight() ? "RCWNN" : "RCWND");
+ break;
+ }
+
+ // Set parameters so we do not get called twice
+ params->param1 = getEntityData(kEntityPlayer)->car;
+ params->param2 = isNight();
+ }
+ }
+
+ if (!params->param5) {
+ params->param4 = 2700; // this is the sound file name
+ params->param5 = 0;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ if (getEntities()->isPlayerPosition(kCarRedSleeping, 18)) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 22)) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventMertensBloodJacket);
+ break;
+ }
+ }
+
+ resetParam8();
+ break;
+
+
+ case kActionCallback: {
+ int action = getCallback();
+ switch(action) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(action == 1 ? kEventCoudertBloodJacket : kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverBloodJacket, true);
+ resetParam8();
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventLocomotiveConductorsDiscovered);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice2, true);
+ break;
+
+ case 6:
+ getAction()->playAnimation(kEventCathBreakCeiling);
+ getObjects()->update(kObjectCeiling, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ getScenes()->processScene();
+ break;
+
+ case 7:
+ getAction()->playAnimation(kEventCathJumpDownCeiling);
+ getScenes()->loadSceneFromPosition(kCarKronos, 89);
+ break;
+
+ case 8:
+ getAction()->playAnimation(kEventCloseMatchbox);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 51);
+ break;
+ }
+ break;
+ }
+
+ case kAction191070912:
+ ENTITY_PARAM(0, 7) = savepoint.param.intValue;
+ break;
+
+ case kActionTrainStopRunning:
+ params->param3 = 0;
+ getEntities()->clearSequences(kEntityTrain);
+ break;
+
+ case kActionCatchBeetle:
+ setCallback(8);
+ setup_savegame(kSavegameTypeEvent, kEventCloseMatchbox);
+ break;
+
+ case kAction203339360:
+ if (params->param7) {
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventLocomotiveConductorsDiscovered);
+ } else {
+ params->param7 = 1;
+ getAction()->playAnimation(kEventLocomotiveConductorsLook);
+ getScenes()->loadSceneFromPosition(kCarCoalTender, 2);
+ }
+ break;
+
+ case kActionTrainStartRunning:
+ if (!params->param3) {
+ params->param1 = 0;
+ params->param3 = 1;
+ getSavePoints()->push(kEntityTrain, kEntityTrain, kActionDrawScene);
+ }
+ break;
+
+ case kAction203863200:
+ if (!strcmp(savepoint.param.charValue, "")) {
+ params->param8 = 1;
+ strcpy((char *)&params1->seq, savepoint.param.charValue); // this is the sound file name
+ }
+ break;
+
+ case kAction222746496:
+ switch(savepoint.param.intValue) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartmentA) ? kPosition_8200 : kPosition_7500;
+ params1->param3 = kPosition_7850;
+ break;
+
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment3 || savepoint.param.intValue == kObjectCompartmentC) ? kPosition_6470 : kPosition_5790;
+ params1->param3 = kPosition_6130;
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment5 || savepoint.param.intValue == kObjectCompartmentE) ? kPosition_4840 : kPosition_4070;
+ params1->param3 = kPosition_4455;
+ break;
+
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ case kObjectCompartmentG:
+ case kObjectCompartmentH:
+ params1->param1 = (savepoint.param.intValue == kObjectCompartment1 || savepoint.param.intValue == kObjectCompartment2) ? kCarGreenSleeping : kCarRedSleeping;
+ params1->param2 = (savepoint.param.intValue == kObjectCompartment7 || savepoint.param.intValue == kObjectCompartmentG) ? kPosition_3050 : kPosition_2740;
+ params1->param3 = kPositionNone;
+ break;
+ }
+ break;
+
+ case kActionBreakCeiling:
+ setCallback(6);
+ setup_savegame(kSavegameTypeEvent, kEventCathBreakCeiling);
+ break;
+
+ case kActionJumpDownCeiling:
+ setCallback(7);
+ setup_savegame(kSavegameTypeEvent, kEventCathJumpDownCeiling);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Train::handleCompartmentAction() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+
+ if (params->param8)
+ getSavePoints()->push(kEntityTrain, kEntityMahmud, kAction290410610, params->param1);
+
+ getAction()->handleOtherCompartment((ObjectIndex)params->param1, false, !params->param8);
+
+ ENTITY_PARAM(0, 8) = params->param1;
+
+ CALLBACK_ACTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void Train::resetParam8() {
+ EXPOSE_PARAMS(EntityData::EntityParametersIIII)
+ EntityData::EntityParametersIIIS *params1 = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters(1);
+
+ if (params->param8
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param2)
+ && !getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params1->param1, (EntityPosition)params1->param3)) {
+
+ if (getSound()->isBuffered((const char *)&params1->seq))
+ getSound()->processEntry((const char *)&params1->seq);
+
+ params->param8 = 0;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/train.h b/engines/lastexpress/entities/train.h
new file mode 100644
index 0000000000..95cb0f28bd
--- /dev/null
+++ b/engines/lastexpress/entities/train.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_TRAIN_H
+#define LASTEXPRESS_TRAIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Train : public Entity {
+public:
+ Train(LastExpressEngine *engine);
+ ~Train() {}
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Harem events
+ *
+ * @param compartment The compartment to handle
+ * @param counter ??? (checked to decide which sound to make when knocking)
+ */
+ DECLARE_FUNCTION_2(harem, ObjectIndex compartment, uint32 counter)
+
+ /**
+ * Handles Train events
+ */
+ DECLARE_FUNCTION(process)
+
+private:
+ // Helper methods
+ void resetParam8();
+ void handleCompartmentAction();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_TRAIN_H
diff --git a/engines/lastexpress/entities/vassili.cpp b/engines/lastexpress/entities/vassili.cpp
new file mode 100644
index 0000000000..64327c2534
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.cpp
@@ -0,0 +1,589 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/vassili.h"
+
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/coudert.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vassili::Vassili(LastExpressEngine *engine) : Entity(engine, kEntityVassili) {
+ ADD_CALLBACK_FUNCTION(Vassili, reset);
+ ADD_CALLBACK_FUNCTION(Vassili, draw);
+ ADD_CALLBACK_FUNCTION(Vassili, savegame);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, function6);
+ ADD_CALLBACK_FUNCTION(Vassili, function7);
+ ADD_CALLBACK_FUNCTION(Vassili, function8);
+ ADD_CALLBACK_FUNCTION(Vassili, function9);
+ ADD_CALLBACK_FUNCTION(Vassili, seizure);
+ ADD_CALLBACK_FUNCTION(Vassili, drawInBed);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter2);
+ ADD_CALLBACK_FUNCTION(Vassili, sleeping);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter3);
+ ADD_CALLBACK_FUNCTION(Vassili, stealEgg);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Vassili, chapter5);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vassili, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vassili, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(3, Vassili, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(4, Vassili, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObject40, kEntityPlayer, kObjectLocationNone, kCursorKeepValue, kCursorKeepValue);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(5, Vassili, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1) {
+ getData()->entityPosition = getEntityData(kEntityTatiana)->entityPosition;
+ getData()->location = getEntityData(kEntityTatiana)->location;
+ } else {
+ if (params->param3 && params->param3 >= getState()->time) {
+ break;
+ }else {
+ params->param3 = (uint)getState()->time + 450;
+ if (params->param3 == 0)
+ break;
+ }
+
+ if (!params->param2 && getObjects()->get(kObjectCompartmentA).location == kObjectLocation1) {
+ params->param2 = 1;
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ }
+ break;
+ }
+ break;
+
+ case kActionDefault:
+ params->param1 = 1;
+ break;
+
+ case kAction122732000:
+ setup_function6();
+ break;
+
+ case kAction168459827:
+ params->param1 = 0;
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Vassili, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM_GOTO(params->param3, getState()->timeTicks, params->param1, label_function7);
+
+ setCallback(1);
+ setup_draw("303B");
+ break;
+ }
+
+ params->param3 = 0;
+
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+
+label_function7:
+ if (params->param4 != kTimeInvalid && getState()->time > kTime1489500) {
+
+ if (getState()->time <= kTime1503000) {
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200) || !params->param4) {
+
+ params->param4 = (uint)getState()->time;
+ if (!params->param4) {
+ setup_function7();
+ break;
+ }
+ }
+
+ if (params->param4 >= getState()->time)
+ break;
+ }
+
+ params->param4 = kTimeInvalid;
+ setup_function7();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ params->param1 = 5 * (3 * rnd(25) + 15);
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+
+ // Shared part with kActionNone
+ goto label_function7;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Vassili, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param1 != kTimeInvalid && getState()->time > kTime1503000) {
+
+ if (getState()->time <= kTime1512000) {
+ if (getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param1) {
+ params->param1 = (uint)getState()->time + 150;
+ if (params->param1) {
+ setup_function8();
+ break;
+ }
+ }
+
+ if (params->param1 >= getState()->time)
+ break;
+ }
+
+ params->param1 = kTimeInvalid;
+ setup_function8();
+ }
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVassili);
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200))
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kAction339669520:
+ setup_function9();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vassili, function8)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ setup_function9();
+ break;
+
+ case kActionDefault:
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, kCarRedSleeping)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, (getEntityData(kEntityPlayer)->car <= kCarRedSleeping) ? 1 : 40);
+ }
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction226031488);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction226031488);
+ getSound()->playSound(kEntityVassili, "VAS1027", SoundManager::kFlagDefault);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vassili, function9)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionEndSound:
+ if (!getEntities()->isDistanceBetweenEntities(kEntityVassili, kEntityPlayer, 2500))
+ getSound()->playSound(kEntityPlayer, "BUMP");
+
+ setup_seizure();
+ break;
+
+ case kActionDefault:
+ case kActionDrawScene:
+ if ((getObjects()->get(kObjectCompartmentA).location == kObjectLocation2 && getEntities()->isPlayerPosition(kCarRedSleeping, 17))
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 18)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 37)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 38)
+ || getEntities()->isPlayerPosition(kCarRedSleeping, 41)) {
+
+ if (savepoint.action == kActionDrawScene)
+ getSound()->processEntry(kEntityVassili);
+
+ setup_seizure();
+ } else {
+ if (savepoint.action == kActionDefault)
+ getSound()->playSound(kEntityVassili, "VAS1028", SoundManager::kFlagDefault);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Vassili, seizure)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ // Check that we have removed the body from the train and changed jacket
+ if (!getProgress().eventCorpseMovedFromFloor) {
+ getAction()->playAnimation(kEventMertensCorpseFloor);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (!getProgress().eventCorpseThrown) {
+ getAction()->playAnimation(kEventMertensCorpseBed);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ if (getProgress().jacket == kJacketBlood) {
+ getAction()->playAnimation(kEventMertensBloodJacket);
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, false);
+ break;
+ }
+
+ // Setup Anna & Coudert
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_function37);
+ RESET_ENTITY_STATE(kEntityCoudert, Coudert, setup_function38);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliSeizure);
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getData()->location = kLocationInsideCompartment;
+ getAction()->playAnimation(kEventVassiliSeizure);
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getProgress().field_18 = 2;
+
+ getSavePoints()->push(kEntityVassili, kEntityAnna, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityVerges, kAction191477936);
+ getSavePoints()->push(kEntityVassili, kEntityCoudert, kAction191477936);
+ getScenes()->loadSceneFromObject(kObjectCompartmentA);
+
+ setup_drawInBed();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vassili, drawInBed)
+ if (savepoint.action == kActionDefault)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vassili, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_sleeping();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vassili, sleeping)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vassili, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_stealEgg();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vassili, stealEgg)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventVassiliCompartmentStealEgg);
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_7850)
+ && getInventory()->hasItem(kItemFirebird)
+ && !getEvent(kEventVassiliCompartmentStealEgg))
+ getObjects()->update(kObject48, kEntityVassili, kObjectLocationNone, kCursorNormal, kCursorHand);
+ else
+ getObjects()->update(kObject48, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+
+ case 2:
+ getAction()->playAnimation(kEventVassiliCompartmentStealEgg);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 67);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vassili, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentA, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getObjects()->updateLocation2(kObjectCompartmentA, kObjectLocation1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Looks identical to sleeping (#13)
+IMPLEMENT_FUNCTION(17, Vassili, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_8200)) {
+ UPDATE_PARAM(params->param3, getState()->timeTicks, params->param1);
+
+ setCallback(1);
+ setup_draw("303B");
+ } else {
+ params->param3 = 0;
+ if (params->param2)
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ }
+ break;
+
+ case kActionDefault:
+ params->param5 = 5 * (3 * rnd(25) + 15);
+ getEntities()->drawSequenceLeft(kEntityVassili, "303A");
+ break;
+
+ case kActionCallback:
+ if (getCallback() != 1)
+ break;
+
+ getEntities()->drawSequenceLeft(kEntityVassili, "303C");
+ params->param1 = 5 * (3 * rnd(25) + 15);
+ params->param2 = 1;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vassili, chapter5)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVassili);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vassili.h b/engines/lastexpress/entities/vassili.h
new file mode 100644
index 0000000000..1862069e25
--- /dev/null
+++ b/engines/lastexpress/entities/vassili.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_VASSILI_H
+#define LASTEXPRESS_VASSILI_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vassili : public Entity {
+public:
+ Vassili(LastExpressEngine *engine);
+ ~Vassili() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+ DECLARE_FUNCTION(function8)
+ DECLARE_FUNCTION(function9)
+ DECLARE_FUNCTION(seizure)
+ DECLARE_FUNCTION(drawInBed)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ DECLARE_FUNCTION(sleeping)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION(stealEgg)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VASSILI_H
diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp
new file mode 100644
index 0000000000..76bf646113
--- /dev/null
+++ b/engines/lastexpress/entities/verges.cpp
@@ -0,0 +1,1898 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/verges.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) {
+ ADD_CALLBACK_FUNCTION(Verges, reset);
+ ADD_CALLBACK_FUNCTION(Verges, draw);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Verges, playSound);
+ ADD_CALLBACK_FUNCTION(Verges, playSound16);
+ ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Verges, savegame);
+ ADD_CALLBACK_FUNCTION(Verges, updateEntity);
+ ADD_CALLBACK_FUNCTION(Verges, function9);
+ ADD_CALLBACK_FUNCTION(Verges, function10);
+ ADD_CALLBACK_FUNCTION(Verges, function11);
+ ADD_CALLBACK_FUNCTION(Verges, function12);
+ ADD_CALLBACK_FUNCTION(Verges, function13);
+ ADD_CALLBACK_FUNCTION(Verges, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Verges, function15);
+ ADD_CALLBACK_FUNCTION(Verges, function16);
+ ADD_CALLBACK_FUNCTION(Verges, function17);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1);
+ ADD_CALLBACK_FUNCTION(Verges, talkHarem);
+ ADD_CALLBACK_FUNCTION(Verges, talkPassengerList);
+ ADD_CALLBACK_FUNCTION(Verges, talkGendarmes);
+ ADD_CALLBACK_FUNCTION(Verges, function22);
+ ADD_CALLBACK_FUNCTION(Verges, function23);
+ ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain);
+ ADD_CALLBACK_FUNCTION(Verges, function25);
+ ADD_CALLBACK_FUNCTION(Verges, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2);
+ ADD_CALLBACK_FUNCTION(Verges, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Verges, chapter3);
+ ADD_CALLBACK_FUNCTION(Verges, function30);
+ ADD_CALLBACK_FUNCTION(Verges, function31);
+ ADD_CALLBACK_FUNCTION(Verges, function32);
+ ADD_CALLBACK_FUNCTION(Verges, function33);
+ ADD_CALLBACK_FUNCTION(Verges, function34);
+ ADD_CALLBACK_FUNCTION(Verges, function35);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4);
+ ADD_CALLBACK_FUNCTION(Verges, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function38);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5);
+ ADD_CALLBACK_FUNCTION(Verges, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Verges, function41);
+ ADD_CALLBACK_FUNCTION(Verges, function42);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Verges, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Verges, draw)
+ Entity::draw(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(3, Verges, callbackActionOnDirection)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getData()->direction != kDirectionRight)
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExitCompartment:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ if (!params->param1) {
+ getSound()->excuseMe(kEntityVerges);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Verges, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(5, Verges, playSound16)
+ Entity::playSound(savepoint, false, SoundManager::kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Verges, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Verges, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityPlayer, "TRA1113", getSound()->getSoundFlag(kEntityVerges));
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(9, Verges, function9)
+switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, (char *)&params->seq1);
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+
+ setCallback(5);
+ setup_updateFromTime(225);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param7) {
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2)) {
+ CALLBACK_ACTION();
+ break;
+ }
+
+ if (params->param6) {
+ UPDATE_PARAM(params->param8, getState()->timeTicks, 75);
+
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+
+ params->param6 = 0;
+ params->param8 = 0;
+ }
+ break;
+
+ case kActionEndSound:
+ params->param6 = 1;
+ break;
+
+ case kActionDefault:
+ if (!getSound()->isBuffered(kEntityVerges)) {
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ params->param7 = 1;
+ }
+
+ if (getEntities()->updateEntity(kEntityVerges, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Verges, function11)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_updateEntity(kCarRestaurant, kPosition_540);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 2:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(3);
+ setup_draw("813US");
+ break;
+
+ case 3:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(4);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 4: {
+ getEntities()->clearSequences(kEntityVerges);
+
+ bool loadscene = true;
+
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesEscortToDiningCar);
+ else if (getEntities()->isInBaggageCar(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesBaggageCarOffLimits);
+ else if (getEntities()->isInKitchen(kEntityPlayer))
+ getAction()->playAnimation(kEventVergesCanIHelpYou);
+ else
+ loadscene = false;
+
+ if (loadscene) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Verges, function12)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_850;
+ getEntities()->clearSequences(kEntityVerges);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(13, Verges, function13, bool)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventVergesSuitcase);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ if (getEvent(kEventVergesSuitcase) || getEvent(kEventVergesSuitcaseNight) || getEvent(kEventVergesSuitcaseOtherEntry) || getEvent(kEventVergesSuitcaseNightOtherEntry))
+ params->param2 = 1;
+
+ if (isNight() && getProgress().chapter != kChapter1)
+ params->param2 = 1;
+
+ if (params->param1) {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightOtherEntryStart : kEventVergesSuitcaseNightOtherEntry);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseOtherEntryStart : kEventVergesSuitcaseOtherEntry);
+ } else {
+ if (isNight())
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseNightStart : kEventVergesSuitcaseNight);
+ else
+ getAction()->playAnimation(params->param2 ? kEventVergesSuitcaseStart : kEventVergesSuitcase);
+ }
+
+ getEntities()->clearSequences(kEntityVerges);
+ getScenes()->loadSceneFromPosition(kCarBaggage, 91);
+
+ CALLBACK_ACTION();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (params->param5 && params->param6) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ params->param5 = 1;
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param6 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (CURRENT_PARAM(1, 1) && params->param8) {
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction125499160);
+
+ if (!getEntities()->isPlayerPosition(kCarGreenSleeping, 2) && !getEntities()->isPlayerPosition(kCarRedSleeping, 2))
+ getData()->entityPosition = kPosition_2088;
+
+ CALLBACK_ACTION();
+ }
+ break;
+
+ case kActionEndSound:
+ CURRENT_PARAM(1, 1)++;
+
+ if (CURRENT_PARAM(1, 1) == 1)
+ getSound()->playSound(kEntityVerges, (char *)&params->seq2);
+ break;
+
+ case kActionDefault:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620F");
+ getSavePoints()->push(kEntityVerges, (EntityIndex)params->param1, kAction171394341);
+ break;
+
+ case kAction155853632:
+ params->param8 = 1;
+ break;
+
+ case kAction202558662:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ getSound()->playSound(kEntityVerges, (char *)&params->seq1);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Verges, function17)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "TRA1291");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ ENTITY_PARAM(0, 3) = 0;
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Verges, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVerges, kActionDeliverMessageToTyler, 0);
+ getSavePoints()->addData(kEntityVerges, kAction226031488, 1);
+ getSavePoints()->addData(kEntityVerges, kAction339669520, 1);
+ getSavePoints()->addData(kEntityVerges, kAction167854368, 4);
+ getSavePoints()->addData(kEntityVerges, kAction158617345, 2);
+ getSavePoints()->addData(kEntityVerges, kAction168255788, 3);
+ getSavePoints()->addData(kEntityVerges, kAction201431954, 5);
+ getSavePoints()->addData(kEntityVerges, kAction168187490, 6);
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(19, Verges, talkHarem)
+ talk(savepoint, "TRA1202", "TRA1201");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Verges, talkPassengerList)
+ talk(savepoint, "TRA1205", "TRA1206");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Verges, talkGendarmes)
+ talk(savepoint, "TRA1250", "TRA1251");
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Verges, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) {
+ setCallback(3);
+ setup_function16(kEntityMertens, "TRA1200", "TRA1201");
+ } else {
+ setCallback(4);
+ setup_function16(kEntityMertens, "TRA1200A", "TRA1201");
+ }
+ break;
+
+ case 3:
+ case 4:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction169633856);
+
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Verges, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction191477936:
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isDistanceBetweenEntities(kEntityVerges, kEntityPlayer, 1000) && getEntityData(kEntityPlayer)->location == kLocationOutsideCompartment) {
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventGendarmesArrestation);
+ }
+ break;
+
+ case kActionEndSound:
+ CALLBACK_ACTION();
+ break;
+
+ case kActionDefault:
+ getSound()->playSound(kEntityVerges, "POL1101", SoundManager::kFlagDefault);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getSound()->processEntry(kEntityVerges);
+ getAction()->playAnimation(kEventGendarmesArrestation);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneGameOverPolice1, true);
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Verges, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+
+ if (!getEntities()->isInKronosSalon(kEntityPlayer)) {
+
+ if (getEntityData(kEntityPlayer)->car > kCarRedSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarRedSleeping && getEntityData(kEntityPlayer)->entityPosition > kPosition_9270)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9270;
+ } else {
+ if (getEntityData(kEntityPlayer)->car > kCarGreenSleeping
+ || (getEntityData(kEntityPlayer)->car == kCarGreenSleeping && getEntityData(kEntityPlayer)->entityPosition < kPosition_4840)) {
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartment5, true);
+ }
+
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_850;
+ }
+
+ getData()->location = kLocationOutsideCompartment;
+
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocation1, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ if (getEntities()->isOutsideAnnaWindow())
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 49);
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4840)
+ || getEntities()->isInsideCompartment(kEntityPlayer, kCarRedSleeping, kPosition_4455)) {
+ getAction()->playAnimation(isNight() ? kEventCathTurningNight : kEventCathTurningDay);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromObject(kObjectCompartmentE, true);
+ }
+
+ getSavePoints()->push(kEntityVerges, kEntityGendarmes, kAction169499649);
+
+ getProgress().field_3C = 1;
+ getState()->timeDelta = 1;
+
+ if (getData()->car == kCarRedSleeping) {
+ setCallback(6);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005");
+ } else {
+ setCallback(7);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ }
+ break;
+ }
+ // Fallback to next case
+
+ case 2:
+ if (getEvent(kEventKronosConversation)) {
+ getProgress().field_3C = 1;
+ getData()->car = kCarGreenSleeping;
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(3);
+ setup_policeGettingOffTrain();
+ } else {
+ setCallback(2);
+ setup_updateFromTime(150);
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(4);
+ setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case 5:
+ case 11:
+ ENTITY_PARAM(0, 7) = 0;
+
+ CALLBACK_ACTION();
+ break;
+
+ case 6:
+ case 7:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 8:
+ getSavePoints()->push(kEntityVerges, kEntityChapters, kAction169629818);
+
+ setCallback(9);
+ setup_policeGettingOffTrain();
+ break;
+
+ case 9:
+ getObjects()->update(kObjectRestaurantCar, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ getObjects()->update(kObjectCompartmentE, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872);
+
+ setCallback(10);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006");
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_function11();
+ break;
+ }
+ break;
+
+ case kAction168710784:
+ getData()->car = kCarGreenSleeping;
+
+ if (!(getEntityData(kEntityPlayer)->car == kCarGreenSleeping))
+ getData()->car = kCarRedSleeping;
+
+ getData()->entityPosition = kPosition_8200;
+ getData()->location = kLocationOutsideCompartment;
+
+ getState()->timeDelta = 3;
+
+ setCallback(8);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Verges, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (ENTITY_PARAM(0, 6)) {
+ params->param1 = 1;
+ params->param2 = 1;
+ params->param3 = 1;
+ params->param4 = 1;
+ params->param5 = 1;
+ params->param6 = 1;
+
+ ENTITY_PARAM(0, 6) = 0;
+ }
+
+ if (ENTITY_PARAM(0, 2)) {
+ setCallback(1);
+ setup_function23();
+ break;
+ }
+
+label_callback1:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(2);
+ setup_function13(false);
+ break;
+ }
+
+label_callback2:
+ if (ENTITY_PARAM(0, 7)) {
+ setCallback(3);
+ setup_function25();
+ break;
+ }
+
+label_callback3:
+ if (params->param6)
+ goto label_callback12;
+
+ TIME_CHECK_CALLBACK_1(kTimeChapter1, params->param7, 4, setup_function9, "TRA1001");
+
+label_callback4:
+ TIME_CHECK_CALLBACK(kTime1089000, params->param8, 5, setup_function12);
+
+ params->param8 = 1;
+
+ if (!params->param5) {
+ setCallback(5);
+ setup_function12();
+ break;
+ }
+
+label_callback8:
+ TIME_CHECK_CALLBACK_1(kTime1107000, CURRENT_PARAM(1, 1), 9, setup_function9, "TRA1001A");
+
+label_callback9:
+ TIME_CHECK_CALLBACK_1(kTime1134000, CURRENT_PARAM(1, 2), 10, setup_function9, "TRA1002");
+
+label_callback10:
+ TIME_CHECK_CALLBACK_1(kTime1165500, CURRENT_PARAM(1, 3), 11, setup_function9, "TRA1003");
+
+label_callback11:
+ TIME_CHECK_CALLBACK_1(kTime1225800, CURRENT_PARAM(1, 4), 12, setup_function9, "TRA1004");
+
+label_callback12:
+ if (ENTITY_PARAM(0, 5) && !params->param2) {
+ setCallback(13);
+ setup_talkGendarmes();
+ break;
+ }
+
+label_callback13:
+ if (getInventory()->hasItem(kItemPassengerList) && !params->param3 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(14);
+ setup_talkPassengerList();
+ break;
+ }
+
+label_callback14:
+ if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) {
+ setCallback(15);
+ setup_function17();
+ break;
+ }
+
+label_callback15:
+ if (ENTITY_PARAM(0, 1) && !params->param5) {
+ if (getState()->time < kTime1134000 || getState()->time > kTime1156500) {
+ setCallback(16);
+ setup_function22();
+ }
+ }
+ break;
+
+ case kActionOpenDoor:
+ setCallback(17);
+ setup_function13(savepoint.param.intValue < 106 ? true : false);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getEntities()->clearSequences(kEntityVerges);
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback1;
+
+ case 2:
+ goto label_callback2;
+
+ case 3:
+ goto label_callback3;
+
+ case 4:
+ goto label_callback4;
+
+ case 5:
+ setCallback(6);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_function15(kEntityMertens, "TRA1202");
+ break;
+
+ case 7:
+ setCallback(8);
+ setup_function11();
+ break;
+
+ case 8:
+ goto label_callback8;
+
+ case 9:
+ goto label_callback9;
+
+ case 10:
+ goto label_callback10;
+
+ case 11:
+ goto label_callback11;
+
+ case 12:
+ goto label_callback12;
+
+ case 13:
+ params->param2 = 1;
+ goto label_callback13;
+
+ case 14:
+ params->param3 = 1;
+ goto label_callback14;
+
+ case 15:
+ params->param4 = 1;
+ goto label_callback15;
+
+ case 16:
+ params->param5 = 1;
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Verges, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Verges, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ }
+
+label_callback_1:
+ TIME_CHECK_CALLBACK_1(kTime1818900, params->param1, 2, setup_function9, "Tra2177");
+
+label_callback_2:
+ if (params->param2 == kTimeInvalid || !getState()->time)
+ goto label_callback_6;
+
+ if (getState()->time > kTime1836000) {
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !params->param2) {
+ params->param2 = (uint)getState()->time;
+
+ if (!params->param2) {
+ setCallback(3);
+ setup_function12();
+ break;
+ }
+ }
+
+ if (params->param2 >= getState()->time) {
+label_callback_6:
+
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(7);
+ setup_function17();
+ }
+
+ break;
+ }
+
+ params->param2 = kTimeInvalid;
+ setCallback(3);
+ setup_function12();
+ break;
+
+ case kActionOpenDoor:
+ setCallback(8);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityCoudert, "TRA2100");
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ goto label_callback_6;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Verges, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_function23();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_540;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 4) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(30, Verges, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, (char *)&params->seq1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(31, Verges, function31)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, "TRA3015");
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function11();
+ break;
+
+ case 4:
+ getProgress().field_48 = 1;
+ ENTITY_PARAM(0, 4) = 0;
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(32, Verges, function32)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK_3(kTime2263500, params->param1, 5, setup_function10, kCarRedSleeping, kPosition_9460, "TRA3006");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ if (getEntities()->isInBaggageCar(kEntityPlayer) || getEntities()->isInKitchen(kEntityPlayer)) {
+ getAction()->playAnimation(getEntities()->isInBaggageCar(kEntityPlayer) ? kEventVergesBaggageCarOffLimits : kEventVergesCanIHelpYou);
+ getSound()->playSound(kEntityPlayer, "BUMP");
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 65);
+ }
+
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5900;
+
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_8500;
+ getData()->location = kLocationOutsideCompartment;
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ setCallback(2);
+ setup_draw("813DD");
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "TRA3004");
+
+ getEntities()->drawSequenceRight(kEntityVerges, "813DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004");
+ break;
+
+ case 4:
+ getEntities()->clearSequences(kEntityVerges);
+ break;
+
+ case 5:
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(33, Verges, function33)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("813US");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVerges, "813UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVerges);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ getEntities()->clearSequences(kEntityVerges);
+ getData()->location = kLocationInsideCompartment;
+ getData()->entityPosition = kPosition_5799;
+
+ setCallback(getProgress().field_3C ? 4 : 5);
+ setup_playSound(getProgress().field_3C ? "ABB3035A" : "ABB3035");
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_playSound("ABB3035");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567);
+
+ setCallback(6);
+ setup_function9("Tra3010");
+ break;
+
+ case 6:
+ setup_function34();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(34, Verges, function34)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 4)) {
+ setCallback(2);
+ setup_function31();
+ break;
+ }
+
+label_callback_2:
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(3);
+ setup_function17();
+ break;
+ }
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime1971000, params->param1, 4, setup_function9, "Tra3001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime1998000, params->param2, 5, setup_function9, "Tra3010a");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK(kTime2016000, params->param3, 6, setup_function35);
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2070000, params->param4, 7, setup_function9, "Tra3002");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2142000, params->param5, 8, setup_function9, "Tra3003");
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2173500, params->param6, 9, setup_function30, "Tra3012");
+
+label_callback_9:
+ TIME_CHECK_CALLBACK(kTime2218500, params->param7, 10, setup_function32);
+ break;
+
+ case kActionOpenDoor:
+ setCallback(11);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+
+ case 9:
+ goto label_callback_9;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(35, Verges, function35)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityMertens, "Tra3011A");
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction188570113);
+
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, "Tra3011");
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityVerges, kEntityMertens, kAction188635520);
+
+ setCallback(6);
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(36, Verges, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarBaggage;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityVerges, kObjectLocationNone, kCursorNormal, kCursorHand);
+
+ ENTITY_PARAM(0, 3) = 0;
+ ENTITY_PARAM(0, 6) = 0;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(37, Verges, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) {
+ setCallback(1);
+ setup_function13(false);
+ break;
+ }
+
+label_callback_1:
+ if (ENTITY_PARAM(0, 6)) {
+ if (ENTITY_PARAM(0, 3)) {
+ setCallback(2);
+ setup_function17();
+ break;
+ }
+
+label_callback_2:
+ TIME_CHECK_CALLBACK_1(kTime2349000, params->param1, 3, setup_function9, "Tra1001");
+
+label_callback_3:
+ TIME_CHECK_CALLBACK_1(kTime2378700, params->param2, 4, setup_function9, "Tra4001");
+
+label_callback_4:
+ TIME_CHECK_CALLBACK_1(kTime2403000, params->param3, 5, setup_function9, "Tra1001A");
+
+label_callback_5:
+ TIME_CHECK_CALLBACK_1(kTime2414700, params->param4, 6, setup_function9, "Tra4002");
+
+label_callback_6:
+ TIME_CHECK_CALLBACK_1(kTime2484000, params->param5, 7, setup_function9, "Tra4003");
+
+label_callback_7:
+ TIME_CHECK_CALLBACK_1(kTime2511000, params->param6, 8, setup_function9, "Tra4004");
+ }
+
+label_callback_8:
+ TIME_CHECK_CALLBACK_1(kTime2538000, params->param7, 9, setup_function9, "Tra4005");
+
+ break;
+
+ case kActionOpenDoor:
+ setCallback(10);
+ setup_function13(savepoint.param.intValue < 106);
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarBaggage;
+ getData()->entityPosition = kPosition_5000;
+ getData()->location = kLocationOutsideCompartment;
+
+ getInventory()->setLocationAndProcess(kItem9, kObjectLocation1);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ goto label_callback_1;
+
+ case 2:
+ goto label_callback_2;
+
+ case 3:
+ goto label_callback_3;
+
+ case 4:
+ goto label_callback_4;
+
+ case 5:
+ goto label_callback_5;
+
+ case 6:
+ goto label_callback_6;
+
+ case 7:
+ goto label_callback_7;
+
+ case 8:
+ goto label_callback_8;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(38, Verges, function38)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_6469;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->clearSequences(kEntityVerges);
+ setCallback(2);
+ setup_updateFromTime(1800);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function11();
+ break;
+
+ case 3:
+ setup_chapter4Handler();
+ break;
+ }
+ break;
+
+ case kAction125233040:
+ getData()->entityPosition = kPosition_5790;
+
+ setCallback(1);
+ setup_updateEntity(kCarGreenSleeping, kPosition_540);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(39, Verges, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVerges);
+
+ getData()->entityPosition = kPosition_3650;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObject104, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ getObjects()->update(kObject105, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(40, Verges, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getEntities()->isInSalon(kEntityPlayer) && !getSound()->isBuffered(kEntityVerges))
+ getSound()->playSound(kEntityVerges, "WAT5000");
+ break;
+
+ case kActionOpenDoor:
+ if (getSound()->isBuffered(kEntityVerges))
+ getSound()->processEntry(kEntityVerges);
+
+ if (getSound()->isBuffered("MUS050"))
+ getSound()->processEntry("MUS050");
+
+ getObjects()->update(kObject65, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathFreePassengers);
+ break;
+
+ case kActionDefault:
+ getScenes()->loadSceneFromItemPosition(kItem9);
+ getObjects()->update(kObject65, kEntityVerges, kObjectLocation1, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathFreePassengers);
+ getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5);
+ getScenes()->loadSceneFromPosition(kCarRedSleeping, 40);
+ setup_function41();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(41, Verges, function41)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getObjects()->updateLocation2(kObjectRestaurantCar, kObjectLocation3);
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_9460;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(1);
+ setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001");
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getEntities()->drawSequenceLeft(kEntityVerges, "620E");
+ // Fallback to next case
+
+ case 2:
+ if (getSound()->isBuffered(kEntityVerges)) {
+ setCallback(2);
+ setup_updateFromTime(225);
+ } else {
+ setCallback(3);
+ setup_playSound("Con5001");
+ }
+ break;
+
+ case 3:
+ getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction155991520);
+
+ setCallback(4);
+ setup_updateEntity(kCarBaggageRear, kPosition_9460);
+ break;
+
+ case 4:
+ setup_function42();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(42, Verges, function42)
+ if (savepoint.action == kActionDefault)
+ getEntities()->clearSequences(kEntityVerges);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private functions
+//////////////////////////////////////////////////////////////////////////
+void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *sound2) {
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_function12();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarRedSleeping, kPosition_2000);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_function15(kEntityCoudert, sound1);
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarGreenSleeping, kPosition_2000);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_function15(kEntityMertens, sound2);
+ break;
+
+ case 5:
+ setup_function11();
+ break;
+
+ case 6:
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h
new file mode 100644
index 0000000000..40a43eac9d
--- /dev/null
+++ b/engines/lastexpress/entities/verges.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_VERGES_H
+#define LASTEXPRESS_VERGES_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Verges : public Entity {
+public:
+ Verges(LastExpressEngine *engine);
+ ~Verges() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Plays sound
+ *
+ * @param savepoint The savepoint
+ * - the sound filename
+ */
+ DECLARE_FUNCTION_NOSETUP(playSound16)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION_1(function9, const char *soundName)
+ DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName)
+ DECLARE_FUNCTION(function11)
+ DECLARE_FUNCTION(function12)
+ DECLARE_FUNCTION_1(function13, bool)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName)
+ DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2)
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ DECLARE_FUNCTION_NOSETUP(talkHarem)
+ DECLARE_FUNCTION(talkPassengerList)
+ DECLARE_FUNCTION(talkGendarmes)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+ DECLARE_FUNCTION(policeGettingOffTrain)
+ DECLARE_FUNCTION(function25)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ DECLARE_FUNCTION_1(function30, const char *soundName)
+ DECLARE_FUNCTION(function31)
+ DECLARE_FUNCTION(function32)
+ DECLARE_FUNCTION(function33)
+ DECLARE_FUNCTION(function34)
+ DECLARE_FUNCTION(function35)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function38)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function41)
+ DECLARE_FUNCTION(function42)
+
+private:
+ void talk(const SavePoint &savepoint, const char *sound1, const char *sound2);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VERGES_H
diff --git a/engines/lastexpress/entities/vesna.cpp b/engines/lastexpress/entities/vesna.cpp
new file mode 100644
index 0000000000..0e8d3bda12
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.cpp
@@ -0,0 +1,1161 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/vesna.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Vesna::Vesna(LastExpressEngine *engine) : Entity(engine, kEntityVesna) {
+ ADD_CALLBACK_FUNCTION(Vesna, reset);
+ ADD_CALLBACK_FUNCTION(Vesna, playSound);
+ ADD_CALLBACK_FUNCTION(Vesna, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Vesna, draw);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity);
+ ADD_CALLBACK_FUNCTION(Vesna, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Vesna, updateEntity2);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionRestaurantOrSalon);
+ ADD_CALLBACK_FUNCTION(Vesna, callbackActionOnDirection);
+ ADD_CALLBACK_FUNCTION(Vesna, savegame);
+ ADD_CALLBACK_FUNCTION(Vesna, function11);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function14);
+ ADD_CALLBACK_FUNCTION(Vesna, function15);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function18);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function21);
+ ADD_CALLBACK_FUNCTION(Vesna, function22);
+ ADD_CALLBACK_FUNCTION(Vesna, function23);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter4);
+ ADD_CALLBACK_FUNCTION(Vesna, function25);
+ ADD_CALLBACK_FUNCTION(Vesna, function26);
+ ADD_CALLBACK_FUNCTION(Vesna, function27);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5);
+ ADD_CALLBACK_FUNCTION(Vesna, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Vesna, function30);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Vesna, reset)
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(2, Vesna, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(3, Vesna, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(4, Vesna, draw)
+ Entity::draw(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Vesna, updateEntity, CarIndex, EntityPosition)
+ if (savepoint.action == kActionExcuseMeCath) {
+ getSound()->playSound(kEntityPlayer, rnd(2) ? "CAT10150" : "CAT1015A");
+
+ return;
+ }
+
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_I(6, Vesna, updateFromTime, uint32)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(7, Vesna, updateEntity2, CarIndex, EntityPosition)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ params->param3 = 0;
+
+ if (getEntities()->isDistanceBetweenEntities(kEntityVesna, kEntityMilos, 500)
+ || (((getData()->direction == kDirectionUp && (getData()->car > getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition > getEntityData(kEntityMilos)->entityPosition)))
+ || (((getData()->direction == kDirectionDown && (getData()->car < getEntityData(kEntityMilos)->car)) || (getData()->car == getEntityData(kEntityMilos)->car && getData()->entityPosition < getEntityData(kEntityMilos)->entityPosition)))) {
+ getData()->field_49B = 0;
+ params->param3 = 1;
+ }
+
+ if (!params->param3)
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kActionDefault:
+ getEntities()->updateEntity(kEntityVesna, (CarIndex)params->param1, (EntityPosition)params->param2);
+ break;
+
+ case kAction123668192:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Vesna, callbackActionRestaurantOrSalon)
+ Entity::callbackActionRestaurantOrSalon(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Vesna, callbackActionOnDirection)
+ Entity::callbackActionOnDirection(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(10, Vesna, savegame, SavegameType, uint32)
+ Entity::savegame(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Vesna, function11)
+ // Expose parameters as IIIS and ignore the default exposed parameters
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (parameters->param3) {
+ UPDATE_PARAM(parameters->param7, getState()->timeTicks, 75);
+
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param7 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ parameters->param1++;
+ switch (parameters->param1) {
+ default:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param1 = 0;
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param2 || parameters->param3) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+
+ parameters->param2 = 0;
+ parameters->param3 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param3 = 1;
+ break;
+
+ case 4:
+ parameters->param2 = 1;
+ parameters->param3 = 0;
+ break;
+ }
+ break;
+
+ case kAction55996766:
+ case kAction101687594:
+ CALLBACK_ACTION();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Vesna, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getSavePoints()->addData(kEntityVesna, kAction124190740, 0);
+
+ getData()->entityPosition = kPosition_4689;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Vesna, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ getData()->entityPosition = getEntityData(kEntityMilos)->entityPosition;
+ getData()->location = getEntityData(kEntityMilos)->location;
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getEntities()->clearSequences(kEntityVesna);
+ setup_function14();
+ }
+ break;
+
+ case kAction204832737:
+ setCallback(1);
+ setup_updateEntity2(kCarRedSleeping, kPosition_3050);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Vesna, function14)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ break;
+
+ case kAction190412928:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Vesna, function15)
+ if (savepoint.action == kActionDefault) {
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Vesna, chapter2)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter2Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Vesna, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kAction135024800:
+ setCallback(2);
+ setup_function18();
+ break;
+
+ case kAction137165825:
+ setCallback(1);
+ setup_function11();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Vesna, function18)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG);
+ break;
+
+ case 11:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Vesna, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Vesna, chapter3Handler)
+ EntityData::EntityParametersIIIS *parameters = (EntityData::EntityParametersIIIS*)_data->getCurrentParameters();
+
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getProgress().field_54 && parameters->param7 != kTimeInvalid) {
+ if (getState()->time > kTime2250000) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+
+ if (!getEntities()->isPlayerInCar(kCarRedSleeping) || !parameters->param7)
+ parameters->param7 = (uint)getState()->time;
+
+ if (parameters->param7 < getState()->time) {
+ parameters->param7 = kTimeInvalid;
+ setup_function22();
+ break;
+ }
+ }
+
+ if (parameters->param2) {
+ UPDATE_PARAM(parameters->param8, getState()->timeTicks, 75);
+
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorNormal, kCursorNormal);
+ }
+
+ parameters->param8 = 0;
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ if (parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(4);
+ setup_playSound(getSound()->wrongDoorCath());
+ break;
+ }
+
+ ++parameters->param3;
+
+ switch (parameters->param3) {
+ default:
+ break;
+
+ case 1:
+ strcpy((char *)&parameters->seq, "VES1015A");
+ break;
+
+ case 2:
+ strcpy((char *)&parameters->seq, "VES1015B");
+ break;
+
+ case 3:
+ strcpy((char *)&parameters->seq, "VES1015C");
+ parameters->param3 = 0;
+ break;
+ }
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+
+ setCallback(savepoint.action == kActionKnock ? 2 : 1);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ getEntities()->clearSequences(kEntityVesna);
+ break;
+
+ case kActionDrawScene:
+ if (parameters->param1 || parameters->param2) {
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ parameters->param1 = 0;
+ parameters->param2 = 0;
+ }
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound((char *)&parameters->seq);
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorTalk, kCursorNormal);
+ parameters->param2 = 1;
+ break;
+
+ case 4:
+ parameters->param1 = 1;
+ parameters->param2 = 0;
+ break;
+ }
+ break;
+
+ case kAction137165825:
+ setCallback(5);
+ setup_function11();
+ break;
+
+ case kAction155913424:
+ setCallback(6);
+ setup_function21();
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Vesna, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ setCallback(6);
+ setup_updateFromTime(4500);
+ break;
+
+ case 6:
+ setCallback(7);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 7:
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(8);
+ setup_draw("808DD");
+ break;
+
+ case 8:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(9);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 10:
+ setCallback(11);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 11:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(22, Vesna, function22)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction259125998);
+
+ setCallback(1);
+ setup_enterExitCompartment("610Bg", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->car = kCarBaggage;
+ getSavePoints()->push(kEntityVesna, kEntityAnna, kAction235856512);
+ break;
+
+ case 6:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(7);
+ setup_draw("808DD");
+ break;
+
+ case 7:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(8);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 8:
+ setCallback(9);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 9:
+ setCallback(10);
+ setup_enterExitCompartment("610Ag", kObjectCompartmentG); /* BUG the original engine passes 3050 here */
+ break;
+
+ case 10:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityVesna);
+
+ setup_function23();
+ break;
+ }
+ break;
+
+ case kAction189299008:
+ setCallback(6);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(23, Vesna, function23)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionKnock:
+ case kActionOpenDoor:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorNormal, kCursorNormal);
+ setCallback(savepoint.action == kActionKnock ? 1 : 2);
+ setup_playSound(savepoint.action == kActionKnock ? "LIB012" : "LIB013");
+ break;
+
+ case kActionDefault:
+ getData()->car = kCarRedSleeping;
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ setCallback(3);
+ setup_playSound("VES1015A");
+ break;
+
+ case 3:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+ break;
+
+ case kAction203663744:
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(24, Vesna, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setCallback(1);
+ setup_function11();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+
+ getObjects()->update(kObjectCompartmentG, kEntityVesna, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1)
+ setup_function25();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(25, Vesna, function25)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (getState()->time > kTime2428200 && !params->param1) {
+ params->param1 = 1;
+ setup_function26();
+ }
+ break;
+
+ case kActionDefault:
+ getSavePoints()->push(kEntityVesna, kEntityMilos, kAction135600432);
+
+ setCallback(1);
+ setup_enterExitCompartment("610BG", kObjectCompartmentG);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->location = kLocationOutsideCompartment;
+ if (getData()->entityPosition < kPosition_2087)
+ getData()->entityPosition = kPosition_2088;
+
+ setCallback(2);
+ setup_updateEntity(kCarRestaurant, kPosition_850);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case 3:
+ getData()->entityPosition = kPosition_1540;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(4);
+ setup_draw("808US");
+ break;
+
+ case 4:
+ getEntities()->drawSequenceRight(kEntityVesna, "808UD");
+ if (getEntities()->isInSalon(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(5);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 5:
+ getEntities()->clearSequences(kEntityVesna);
+ getData()->entityPosition = kPosition_5900;
+ getData()->location = kLocationInsideCompartment;
+
+ // Original game calls clearSequences a second time
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(26, Vesna, function26)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ setCallback(1);
+ setup_callbackActionRestaurantOrSalon();
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->car = kCarRestaurant;
+ getData()->entityPosition = kPosition_5800;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(2);
+ setup_draw("808DD");
+ break;
+
+ case 2:
+ getEntities()->drawSequenceRight(kEntityVesna, "808DS");
+
+ if (getEntities()->isInRestaurant(kEntityPlayer))
+ getEntities()->updateFrame(kEntityVesna);
+
+ setCallback(3);
+ setup_callbackActionOnDirection();
+ break;
+
+ case 3:
+ setCallback(4);
+ setup_updateEntity(kCarRedSleeping, kPosition_3050);
+ break;
+
+ case 4:
+ setCallback(5);
+ setup_enterExitCompartment("610AG", kObjectCompartmentG);
+ break;
+
+ case 5:
+ setup_function27();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(27, Vesna, function27)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityVesna);
+ getObjects()->update(kObjectCompartmentG, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRedSleeping;
+ getData()->inventoryItem = kItemNone;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(28, Vesna, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityVesna);
+
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(29, Vesna, chapter5Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionOpenDoor:
+ setCallback(1);
+
+ getData()->currentCall++;
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaRestaurantKilled);
+ break;
+
+ case kActionDefault:
+ getObjects()->update(kObject64, kEntityVesna, kObjectLocationNone, kCursorNormal, kCursorForward);
+ break;
+
+ case kActionCallback:
+ if (getCallback() == 1) {
+ getAction()->playAnimation(kEventCathVesnaRestaurantKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ }
+ break;
+
+ case kAction134427424:
+ getObjects()->update(kObject64, kEntityPlayer, kObjectLocationNone, kCursorNormal, kCursorForward);
+ setup_function30();
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(30, Vesna, function30)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ if (!params->param1) {
+ UPDATE_PARAM_PROC(params->param3, getState()->timeTicks, 120)
+ getSound()->playSound(kEntityVesna, "Ves50001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ UPDATE_PARAM_PROC_END
+ }
+
+ UPDATE_PARAM(params->param4, getState()->timeTicks, 180);
+
+ setCallback(1);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ case 2:
+ getAction()->playAnimation(kEventCathVesnaTrainTopKilled);
+ getLogic()->gameOver(kSavegameTypeIndex, 1, kSceneNone, true);
+ break;
+
+ case 3:
+ getAction()->playAnimation(kEventCathVesnaTrainTopFight);
+
+ setCallback(4);
+ setup_savegame(kSavegameTypeTime, kTimeNone);
+ break;
+
+ case 4:
+ params->param2 = getFight()->setup(kFightVesna);
+
+ if (params->param2) {
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneNone, params->param2 != Fight::kFightEndExit);
+ } else {
+ getSound()->playSound(kEntityPlayer, "TUNNEL");
+
+ getState()->time = (TimeValue)(getState()->time + 1800);
+
+ setCallback(5);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopWin);
+ }
+ break;
+
+ case 5:
+ getAction()->playAnimation(kEventCathVesnaTrainTopWin);
+ getScenes()->loadSceneFromPosition(kCarRestaurant, 11);
+
+ setup_nullfunction();
+ break;
+ }
+ break;
+
+ case kAction167992577:
+ setCallback(3);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopFight);
+ break;
+
+ case kAction202884544:
+ if (params->param1) {
+ setCallback(2);
+ setup_savegame(kSavegameTypeEvent, kEventCathVesnaTrainTopKilled);
+ } else {
+ getSound()->playSound(kEntityVesna, "Ves5001", SoundManager::kFlagDefault);
+ params->param1 = 1;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_NULL_FUNCTION(31, Vesna)
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/vesna.h b/engines/lastexpress/entities/vesna.h
new file mode 100644
index 0000000000..55a9a989c5
--- /dev/null
+++ b/engines/lastexpress/entities/vesna.h
@@ -0,0 +1,176 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_VESNA_H
+#define LASTEXPRESS_VESNA_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Vesna : public Entity {
+public:
+ Vesna(LastExpressEngine *engine);
+ ~Vesna() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+
+ /**
+ * Draws the entity
+ *
+ * @param sequence The sequence to draw
+ */
+ DECLARE_FUNCTION_1(draw, const char *sequence)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param time The time to add
+ */
+ DECLARE_FUNCTION_1(updateFromTime, uint32 time)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity2, CarIndex car, EntityPosition entityPosition)
+
+ /**
+ * Process callback action when somebody is standing in the restaurant or salon.
+ */
+ DECLARE_FUNCTION(callbackActionRestaurantOrSalon)
+
+ /**
+ * Process callback action when the entity direction is not kDirectionRight
+ */
+ DECLARE_FUNCTION(callbackActionOnDirection)
+
+ /**
+ * Saves the game
+ *
+ * @param savegameType The type of the savegame
+ * @param param The param for the savegame (EventIndex or TimeValue)
+ */
+ DECLARE_FUNCTION_2(savegame, SavegameType savegameType, uint32 param)
+
+ DECLARE_FUNCTION(function11)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function14)
+ DECLARE_FUNCTION(function15)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ DECLARE_FUNCTION(function18)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ DECLARE_FUNCTION(function21)
+ DECLARE_FUNCTION(function22)
+ DECLARE_FUNCTION(function23)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ DECLARE_FUNCTION(function25)
+ DECLARE_FUNCTION(function26)
+ DECLARE_FUNCTION(function27)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function30)
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_VESNA_H
diff --git a/engines/lastexpress/entities/yasmin.cpp b/engines/lastexpress/entities/yasmin.cpp
new file mode 100644
index 0000000000..7dc46f9690
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.cpp
@@ -0,0 +1,490 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/entities/yasmin.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+namespace LastExpress {
+
+Yasmin::Yasmin(LastExpressEngine *engine) : Entity(engine, kEntityYasmin) {
+ ADD_CALLBACK_FUNCTION(Yasmin, reset);
+ ADD_CALLBACK_FUNCTION(Yasmin, enterExitCompartment);
+ ADD_CALLBACK_FUNCTION(Yasmin, playSound);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateFromTime);
+ ADD_CALLBACK_FUNCTION(Yasmin, updateEntity);
+ ADD_CALLBACK_FUNCTION(Yasmin, function6);
+ ADD_CALLBACK_FUNCTION(Yasmin, function7);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter1Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function10);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter2Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter3Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter4Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function17);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5);
+ ADD_CALLBACK_FUNCTION(Yasmin, chapter5Handler);
+ ADD_CALLBACK_FUNCTION(Yasmin, function20);
+ ADD_CALLBACK_FUNCTION(Yasmin, function21);
+ ADD_NULL_FUNCTION();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(1, Yasmin, reset)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+
+ // Process default actions
+ Entity::reset(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_SI(2, Yasmin, enterExitCompartment, ObjectIndex)
+ Entity::enterExitCompartment(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_S(3, Yasmin, playSound)
+ Entity::playSound(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_NOSETUP(4, Yasmin, updateFromTime)
+ Entity::updateFromTime(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION_II(5, Yasmin, updateEntity, CarIndex, EntityPosition)
+ Entity::updateEntity(savepoint, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(6, Yasmin, function6)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Be", kObjectCompartment5);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_3050);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ag", kObjectCompartment7);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(7, Yasmin, function7)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationOutsideCompartment;
+
+ setCallback(1);
+ setup_enterExitCompartment("615Bg", kObjectCompartment7);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ setCallback(2);
+ setup_updateEntity(kCarGreenSleeping, kPosition_4840);
+ break;
+
+ case 2:
+ setCallback(3);
+ setup_enterExitCompartment("615Ae", kObjectCompartment5);
+ break;
+
+ case 3:
+ getData()->location = kLocationInsideCompartment;
+ getEntities()->clearSequences(kEntityYasmin);
+
+ CALLBACK_ACTION();
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(8, Yasmin, chapter1)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK(kTimeChapter1, params->param1, setup_chapter1Handler);
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_4840;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(9, Yasmin, chapter1Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1093500, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104");
+ TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106");
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_2740;
+ setCallback(2);
+ setup_playSound("Har1102");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime1161000, params->param2, 3, setup_function7);
+ // Fallback to case 3
+
+ case 3:
+ TIME_CHECK_PLAYSOUND_UPDATEPOSITION(kTime1162800, params->param3, 4, "Har1102", kPosition_4070);
+ // Fallback to case 4
+
+ case 4:
+ TIME_CHECK_CALLBACK_1(kTime1165500, params->param4, 5, setup_playSound, "Har1104");
+ // Fallback to case 5
+
+ case 5:
+ TIME_CHECK_CALLBACK_1(kTime1174500, params->param5, 6, setup_playSound, "Har1106");
+ // Fallback to case 6
+
+ case 6:
+ TIME_CHECK_CALLBACK(kTime1183500, params->param6, 7, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(10, Yasmin, function10)
+ if (savepoint.action == kActionDefault) {
+ getObjects()->update(kObjectCompartment7, kEntityPlayer, kObjectLocation3, kCursorHandKnock, kCursorHand);
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+
+ getEntities()->clearSequences(kEntityYasmin);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(11, Yasmin, chapter2)
+ if (savepoint.action == kActionDefault) {
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+
+ setup_chapter2Handler();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(12, Yasmin, chapter2Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime1759500, params->param1, 1, setup_function7);
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+ break;
+
+ case kActionCallback:
+
+ if (getCallback() != 1)
+ break;
+
+ if (getState()->time > kTime1800000 && !params->param2) {
+ params->param2 = 1;
+ getData()->entityPosition = kPosition_4070;
+
+ getSavePoints()->push(kEntityYasmin, kEntityTrain, kAction191070912, kPosition_4070);
+ }
+
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(13, Yasmin, chapter3)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter3Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(14, Yasmin, chapter3Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2062800, params->param1, 1, setup_function6);
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ TIME_CHECK_CALLBACK(kTime2106000, params->param2, 2, setup_function7);
+ // Fallback to case 2
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2160000, params->param3, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(15, Yasmin, chapter4)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter4Handler();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_3050;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(16, Yasmin, chapter4Handler)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ TIME_CHECK_CALLBACK(kTime2457000, params->param1, 1, setup_function7);
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+
+ case kActionCallback:
+ switch (getCallback()) {
+ default:
+ break;
+
+ case 1:
+ getData()->entityPosition = kPosition_4070;
+ setCallback(2);
+ setup_playSound("Har1110");
+ break;
+
+ case 2:
+ TIME_CHECK_CALLBACK(kTime2479500, params->param2, 3, setup_function6);
+ break;
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(17, Yasmin, function17)
+ // Same as existing function 10 ?
+ function10(savepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(18, Yasmin, chapter5)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ setup_chapter5Handler();
+ break;
+
+ case kActionDefault:
+ getEntities()->clearSequences(kEntityYasmin);
+
+ getData()->entityPosition = kPosition_3969;
+ getData()->location = kLocationInsideCompartment;
+ getData()->car = kCarRestaurant;
+ getData()->clothes = kClothesDefault;
+ getData()->inventoryItem = kItemNone;
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(19, Yasmin, chapter5Handler)
+ if (savepoint.action == kActionProceedChapter5)
+ setup_function20();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(20, Yasmin, function20)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ UPDATE_PARAM(params->param1, getState()->time, 2700);
+ setup_function21();
+ break;
+
+ case kActionDefault:
+ getData()->entityPosition = kPosition_2500;
+ getData()->location = kLocationOutsideCompartment;
+ getData()->car = kCarGreenSleeping;
+ break;
+
+ case kActionDrawScene:
+ if (getEntities()->isInsideTrainCar(kEntityPlayer, kCarGreenSleeping)) {
+ setup_function21();
+ }
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+IMPLEMENT_FUNCTION(21, Yasmin, function21)
+ switch (savepoint.action) {
+ default:
+ break;
+
+ case kActionNone:
+ case kActionDefault:
+ if (getEntities()->updateEntity(kEntityYasmin, (CarIndex)params->param1, (EntityPosition)params->param2))
+ CALLBACK_ACTION();
+ break;
+
+ case kActionExcuseMeCath:
+ getSound()->excuseMeCath();
+ break;
+
+ case kActionExcuseMe:
+ getSound()->excuseMe(kEntityYasmin);
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/entities/yasmin.h b/engines/lastexpress/entities/yasmin.h
new file mode 100644
index 0000000000..b35c713f8b
--- /dev/null
+++ b/engines/lastexpress/entities/yasmin.h
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_YASMIN_H
+#define LASTEXPRESS_YASMIN_H
+
+#include "lastexpress/entities/entity.h"
+#include "lastexpress/entities/entity_intern.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Yasmin : public Entity {
+public:
+ Yasmin(LastExpressEngine *engine);
+ ~Yasmin() {}
+
+ /**
+ * Resets the entity
+ */
+ DECLARE_FUNCTION(reset)
+
+ /**
+ * Handles entering/exiting a compartment.
+ *
+ * @param sequence The sequence to draw
+ * @param compartment The compartment
+ */
+ DECLARE_FUNCTION_2(enterExitCompartment, const char *sequence, ObjectIndex compartment)
+
+ /**
+ * Plays sound
+ *
+ * @param filename The sound filename
+ */
+ DECLARE_FUNCTION_1(playSound, const char *filename)
+
+ /**
+ * Updates parameter 2 using time value
+ *
+ * @param savepoint The savepoint
+ * - Time to add
+ */
+ DECLARE_FUNCTION_NOSETUP(updateFromTime)
+
+ /**
+ * Updates the entity
+ *
+ * @param car The car
+ * @param entityPosition The entity position
+ */
+ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition)
+
+ DECLARE_FUNCTION(function6)
+ DECLARE_FUNCTION(function7)
+
+ /**
+ * Setup Chapter 1
+ */
+ DECLARE_FUNCTION(chapter1)
+
+ /**
+ * Handle Chapter 1 events
+ */
+ DECLARE_FUNCTION(chapter1Handler)
+
+ DECLARE_FUNCTION(function10)
+
+ /**
+ * Setup Chapter 2
+ */
+ DECLARE_FUNCTION(chapter2)
+
+ /**
+ * Handle Chapter 2 events
+ */
+ DECLARE_FUNCTION(chapter2Handler)
+
+ /**
+ * Setup Chapter 3
+ */
+ DECLARE_FUNCTION(chapter3)
+
+ /**
+ * Handle Chapter 3 events
+ */
+ DECLARE_FUNCTION(chapter3Handler)
+
+ /**
+ * Setup Chapter 4
+ */
+ DECLARE_FUNCTION(chapter4)
+
+ /**
+ * Handle Chapter 4 events
+ */
+ DECLARE_FUNCTION(chapter4Handler)
+
+ DECLARE_FUNCTION(function17)
+
+ /**
+ * Setup Chapter 5
+ */
+ DECLARE_FUNCTION(chapter5)
+
+ /**
+ * Handle Chapter 5 events
+ */
+ DECLARE_FUNCTION(chapter5Handler)
+
+ DECLARE_FUNCTION(function20)
+ DECLARE_FUNCTION(function21)
+
+ DECLARE_NULL_FUNCTION()
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_YASMIN_H
diff --git a/engines/lastexpress/eventhandler.h b/engines/lastexpress/eventhandler.h
new file mode 100644
index 0000000000..106878fbbd
--- /dev/null
+++ b/engines/lastexpress/eventhandler.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_EVENTHANDLER_H
+#define LASTEXPRESS_EVENTHANDLER_H
+
+#include "common/EventRecorder.h"
+#include "common/func.h"
+
+namespace LastExpress {
+
+#define SET_EVENT_HANDLERS(class, inst) \
+ _engine->setEventHandlers(new EVENT_HANDLER(class, eventMouse, inst), new EVENT_HANDLER(class, eventTick, inst));
+
+#define EVENT_HANDLER(class, name, inst) \
+ Common::Functor1Mem<const Common::Event&, void, class>(inst, &class::name)
+
+class EventHandler {
+public:
+ virtual ~EventHandler() {}
+
+ // Function pointer for event handler
+ typedef Common::Functor1<const Common::Event&, void> EventFunction;
+
+ virtual void eventMouse(const Common::Event &ev) {} // Event type 1
+ virtual void eventTick(const Common::Event &ev) {} // Event type 3
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_EVENTHANDLER_H
diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp
new file mode 100644
index 0000000000..84f406931d
--- /dev/null
+++ b/engines/lastexpress/game/action.cpp
@@ -0,0 +1,1966 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/action.h"
+
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+static const int _animationListSize = 273;
+
+// List of animations
+static const struct {
+ const char *filename;
+ uint16 time;
+} _animationList[_animationListSize] = {
+ {"", 0},
+ {"1002", 255},
+ {"1002D", 255},
+ {"1003", 0},
+ {"1005", 195},
+ {"1006", 750}, // 5
+ {"1006A", 750},
+ {"1008", 765},
+ {"1008N", 765},
+ {"1008A", 750},
+ {"1008AN", 750}, // 10
+ {"1009", 0},
+ {"1011", 1005},
+ {"1011A", 780},
+ {"1012", 300},
+ {"1013", 285},
+ {"1017", 870}, // 15
+ {"1017A", 0}, // Not in the data files?
+ {"1019", 120},
+ {"1019D", 120},
+ {"1020", 120}, // 20
+ {"1022", 525},
+ {"1022A", 180},
+ {"1022AD", 210},
+ {"1022B", 210},
+ {"1022C", 210}, // 25
+ {"1023", 135},
+ {"1025", 945},
+ {"1028", 300},
+ {"1030", 390},
+ {"1031", 375}, // 30
+ {"1032", 1050},
+ {"1033", 945},
+ {"1034", 495},
+ {"1035", 1230},
+ {"1037", 1425}, // 35
+ {"1038", 195},
+ {"1038A", 405},
+ {"1039", 600},
+ {"1040", 945},
+ {"1041", 510}, // 40
+ {"1042", 540},
+ {"1043", 855},
+ {"1044", 645},
+ {"1046", 0},
+ {"1047", 0}, // 45
+ {"1047A", 0},
+ {"1059", 1005},
+ {"1060", 255},
+ {"1063", 0},
+ {"1101", 255}, // 50
+ {"1102", 1320},
+ {"1103", 210},
+ {"1104", 120},
+ {"1105", 1350},
+ {"1106", 315}, // 55
+ {"1106A", 315},
+ {"1106D", 315},
+ {"1107", 1},
+ {"1107A", 660},
+ {"1108", 300}, // 60
+ {"1109", 1305},
+ {"1110", 300},
+ {"1112", 0},
+ {"1115", 0},
+ {"1115A", 0}, // 65
+ {"1115B", 0},
+ {"1115C", 0},
+ {"1115D", 0},
+ {"1115E", 0},
+ {"1115F", 0}, // 70
+ {"1115G", 0},
+ {"1115H", 0},
+ {"1116", 0},
+ {"1117", 0},
+ {"1118", 105}, // 75
+ {"1202", 510},
+ {"1202A", 510},
+ {"1203", 720},
+ {"1204", 120},
+ {"1205", 465}, // 80
+ {"1206", 690},
+ {"1206A", 450},
+ {"1208", 465},
+ {"1210", 1020},
+ {"1211", 600}, // 85
+ {"1212", 435},
+ {"1213", 525},
+ {"1213A", 150},
+ {"1215", 390},
+ {"1216", 0}, // 90
+ {"1219", 240},
+ {"1222", 1095},
+ {"1223", 0},
+ {"1224", 720},
+ {"1225", 1005}, // 95
+ {"1227", 840},
+ {"1227A", 840},
+ {"1303", 450},
+ {"1303N", 450},
+ {"1304", 450}, // 100
+ {"1304N", 450},
+ {"1305", 630},
+ {"1309", 0},
+ {"1311", 1710},
+ {"1312", 240}, // 105
+ {"1312D", 240},
+ {"1313", 930},
+ {"1315", 1035},
+ {"1315A", 1035},
+ {"1401", 540}, // 110
+ {"1402", 150},
+ {"1402B", 150},
+ {"1403", 90},
+ {"1404", 885},
+ {"1404A", 0}, // 115
+ {"1405", 135},
+ {"1406", 1665},
+ {"1501", 285},
+ {"1501A", 285},
+ {"1502", 165}, // 120
+ {"1502A", 165},
+ {"1502D", 165},
+ {"1503", 0},
+ {"1504", 0},
+ {"1505", 0}, // 125
+ {"1505A", 0},
+ {"1506", 300},
+ {"1506A", 180},
+ {"1508", 0},
+ {"1509", 450}, // 130
+ {"1509S", 450},
+ {"1509A", 450},
+ {"1509AS", 450},
+ {"1509N", 450},
+ {"1509SN", 450}, // 135
+ {"1509AN", 450},
+ {"1509BN", 450},
+ {"1511", 150},
+ {"1511A", 150},
+ {"1511B", 90}, // 140
+ {"1511BA", 90},
+ {"1511C", 135},
+ {"1511D", 105},
+ {"1930", 0},
+ {"1511E", 150}, // 145
+ {"1512", 165},
+ {"1513", 180},
+ {"1517", 0},
+ {"1517A", 165},
+ {"1518", 165}, // 150
+ {"1518A", 165},
+ {"1518B", 165},
+ {"1591", 450},
+ {"1592", 450},
+ {"1593", 450}, // 155
+ {"1594", 450},
+ {"1595", 450},
+ {"1596", 450},
+ {"1601", 0},
+ {"1603", 0}, // 160
+ {"1606B", 315},
+ {"1607A", 0},
+ {"1610", 0},
+ {"1611", 0},
+ {"1612", 0}, // 165
+ {"1615", 0},
+ {"1619", 0},
+ {"1620", 120},
+ {"1621", 105},
+ {"1622", 105}, // 170
+ {"1629", 450},
+ {"1630", 450},
+ {"1631", 525},
+ {"1632", 0},
+ {"1633", 615}, // 175
+ {"1634", 180},
+ {"1702", 180},
+ {"1702DD", 180},
+ {"1702NU", 180},
+ {"1702ND", 180}, // 180
+ {"1704", 300},
+ {"1704D", 300},
+ {"1705", 195},
+ {"1705D", 195},
+ {"1706", 195}, // 185
+ {"1706DD", 195},
+ {"1706ND", 195},
+ {"1706NU", 195},
+ {"1901", 135},
+ {"1902", 1410}, // 190
+ {"1903", 0},
+ {"1904", 1920},
+ {"1908", 600},
+ {"1908A", 195},
+ {"1908B", 105}, // 195
+ {"1908C", 165},
+ {"1908CD", 0},
+ {"1909A", 150},
+ {"1909B", 150},
+ {"1909C", 150}, // 200
+ {"1910A", 180},
+ {"1910B", 180},
+ {"1910C", 180},
+ {"1911A", 90},
+ {"1911B", 90}, // 205
+ {"1911C", 90},
+ {"1912", 0},
+ {"1913", 0},
+ {"1917", 0},
+ {"1918", 390}, // 210
+ {"1919", 360},
+ {"1919A", 105},
+ {"1920", 75},
+ {"1922", 75},
+ {"1923", 150}, // 215
+ {"8001", 120},
+ {"8001A", 120},
+ {"8002", 120},
+ {"8002A", 120},
+ {"8002B", 120}, // 220
+ {"8003", 105},
+ {"8003A", 105},
+ {"8004", 105},
+ {"8004A", 105},
+ {"8005", 270}, // 225
+ {"8005B", 270},
+ {"8010", 270},
+ {"8013", 120},
+ {"8013A", 120},
+ {"8014", 165}, // 230
+ {"8014A", 165},
+ {"8014R", 165},
+ {"8014AR", 165},
+ {"8015", 150},
+ {"8015A", 150}, // 235
+ {"8015R", 150},
+ {"8015AR", 150},
+ {"8017", 120},
+ {"8017A", 120},
+ {"8017R", 120}, // 240
+ {"8017AR", 120},
+ {"8017N", 90},
+ {"8023", 135},
+ {"8023A", 135},
+ {"8023M", 135}, // 245
+ {"8024", 150},
+ {"8024A", 180},
+ {"8024M", 180},
+ {"8025", 150},
+ {"8025A", 150}, // 250
+ {"8025M", 150},
+ {"8027", 75},
+ {"8028", 75},
+ {"8029", 120},
+ {"8029A", 120}, // 255
+ {"8031", 375},
+ {"8032", 0},
+ {"8032A", 0},
+ {"8033", 105},
+ {"8035", 195}, // 260
+ {"8035A", 120},
+ {"8035B", 180},
+ {"8035C", 135},
+ {"8036", 105},
+ {"8037", 195}, // 265
+ {"8037A", 195},
+ {"8040", 240},
+ {"8040A", 240},
+ {"8041", 195},
+ {"8041A", 195}, // 270
+ {"8042", 600},
+ {"8042A", 600}
+};
+
+Action::Action(LastExpressEngine *engine) : _engine(engine) {
+ ADD_ACTION(dummy);
+ ADD_ACTION(inventory);
+ ADD_ACTION(savePoint);
+ ADD_ACTION(playSound);
+ ADD_ACTION(playMusic);
+ ADD_ACTION(knock);
+ ADD_ACTION(compartment);
+ ADD_ACTION(playSounds);
+ ADD_ACTION(playAnimation);
+ ADD_ACTION(openCloseObject);
+ ADD_ACTION(updateObjetLocation2);
+ ADD_ACTION(setItemLocation);
+ ADD_ACTION(knockNoSound);
+ ADD_ACTION(pickItem);
+ ADD_ACTION(dropItem);
+ ADD_ACTION(dummy);
+ ADD_ACTION(enterCompartment);
+ ADD_ACTION(dummy);
+ ADD_ACTION(getOutsideTrain);
+ ADD_ACTION(slip);
+ ADD_ACTION(getInsideTrain);
+ ADD_ACTION(climbUpTrain);
+ ADD_ACTION(climbDownTrain);
+ ADD_ACTION(jumpUpDownTrain);
+ ADD_ACTION(unbound);
+ ADD_ACTION(25);
+ ADD_ACTION(26);
+ ADD_ACTION(27);
+ ADD_ACTION(concertSitCough);
+ ADD_ACTION(29);
+ ADD_ACTION(catchBeetle);
+ ADD_ACTION(exitCompartment);
+ ADD_ACTION(32);
+ ADD_ACTION(useWhistle);
+ ADD_ACTION(openMatchBox);
+ ADD_ACTION(openBed);
+ ADD_ACTION(dummy);
+ ADD_ACTION(dialog);
+ ADD_ACTION(eggBox);
+ ADD_ACTION(39);
+ ADD_ACTION(bed);
+ ADD_ACTION(playMusicChapter);
+ ADD_ACTION(playMusicChapterSetupTrain);
+ ADD_ACTION(switchChapter);
+ ADD_ACTION(44);
+}
+
+Action::~Action() {
+ for (int i = 0; i < (int)_actions.size(); i++)
+ delete _actions[i];
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Processing hotspot
+//////////////////////////////////////////////////////////////////////////
+SceneIndex Action::processHotspot(const SceneHotspot &hotspot) {
+ if (!hotspot.action || hotspot.action >= (int)_actions.size())
+ return kSceneInvalid;
+
+ return (*_actions[hotspot.action])(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Actions
+//////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////
+// Action 0
+IMPLEMENT_ACTION(dummy)
+ warning("Action::action_dummy: Dummy action function called (hotspot action: %d)!", hotspot.action);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 1
+IMPLEMENT_ACTION(inventory)
+ if (!getState()->sceneUseBackup)
+ return kSceneInvalid;
+
+ SceneIndex index = kSceneNone;
+ if (getState()->sceneBackup2) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ } else {
+ getState()->sceneUseBackup = false;
+ index = getState()->sceneBackup;
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+ if (getEntities()->getPosition(backup->car, backup->position))
+ index = getScenes()->processIndex(getState()->sceneBackup);
+ }
+
+ getScenes()->loadScene(index);
+
+ if (!getInventory()->getSelectedItem())
+ return kSceneInvalid;
+
+ if (!getInventory()->getSelectedEntry()->isSelectable || (!getState()->sceneBackup2 && getInventory()->getFirstExaminableItem()))
+ getInventory()->selectItem(getInventory()->getFirstExaminableItem());
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 2
+IMPLEMENT_ACTION(savePoint)
+ getSavePoints()->push(kEntityPlayer, (EntityIndex)hotspot.param1, (ActionIndex)hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 3
+IMPLEMENT_ACTION(playSound)
+
+ // Check that the file is not already buffered
+ if (hotspot.param2 || !getSound()->isBuffered(Common::String::printf("LIB%03d", hotspot.param1), true))
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 4
+IMPLEMENT_ACTION(playMusic)
+ // Check that the file is not already buffered
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && (hotspot.param1 != 50 || getProgress().chapter == kChapter5))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 5
+IMPLEMENT_ACTION(knock)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+ } else {
+ if (!getSound()->isBuffered("LIB012", true))
+ getSound()->playSoundEvent(kEntityPlayer, 12);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 6
+IMPLEMENT_ACTION(compartment)
+ ObjectIndex compartment = (ObjectIndex)hotspot.param1;
+
+ if (compartment >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(compartment).entity) {
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(compartment).entity, kActionOpenDoor, compartment);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ if (handleOtherCompartment(compartment, true, true)) {
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ ObjectLocation location = getObjects()->get(compartment).location;
+ if (location == kObjectLocation1 || location == kObjectLocation3 || getEntities()->checkFields2(compartment)) {
+
+ if (location != kObjectLocation1 || getEntities()->checkFields2(compartment)
+ || (getInventory()->getSelectedItem() != kItemKey
+ && (compartment != kObjectCompartment1
+ || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))) {
+ if (!getSound()->isBuffered("LIB13"))
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+
+ // Stop processing further
+ return kSceneNone;
+ }
+
+ getSound()->playSoundEvent(kEntityPlayer, 32);
+
+ if ((compartment >= kObjectCompartment1 && compartment <= kObjectCompartment3) || (compartment >= kObjectCompartmentA && compartment <= kObjectCompartmentF))
+ getObjects()->update(compartment, kEntityPlayer, kObjectLocationNone, kCursorHandKnock, kCursorHand);
+
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+ }
+
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->getSelectedItem() != kItemKey) {
+ if (compartment == kObjectCageMax) {
+ getSound()->playSoundEvent(kEntityPlayer, 26);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+ }
+ return kSceneInvalid;
+ }
+
+ getObjects()->update(kObjectCompartment1, kEntityPlayer, kObjectLocation1, kCursorHandKnock, kCursorHand);
+ getSound()->playSoundEvent(kEntityPlayer, 16);
+ getInventory()->unselectItem();
+
+ // Stop processing further
+ return kSceneNone;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 7
+IMPLEMENT_ACTION(playSounds)
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1);
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param3, hotspot.param2);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 8
+IMPLEMENT_ACTION(playAnimation)
+ if (getEvent(hotspot.param1))
+ return kSceneInvalid;
+
+ playAnimation((EventIndex)hotspot.param1);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 9
+IMPLEMENT_ACTION(openCloseObject)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->update(object, getObjects()->get(object).entity, location, kCursorKeepValue, kCursorKeepValue);
+
+ bool isNotWindow = ((object <= kObjectCompartment8 || object >= kObjectHandleBathroom) && (object <= kObjectCompartmentH || object >= kObject48));
+
+ switch (location) {
+ default:
+ break;
+
+ case kObjectLocation1:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 21);
+ break;
+
+ case kObjectLocation2:
+ if (isNotWindow)
+ getSound()->playSoundEvent(kEntityPlayer, 36);
+ else
+ getSound()->playSoundEvent(kEntityPlayer, 20);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 10
+IMPLEMENT_ACTION(updateObjetLocation2)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ getObjects()->updateLocation2(object, location);
+
+ if (object != kObject112 || getSound()->isBuffered("LIB096")) {
+ if (object == 1)
+ getSound()->playSoundEvent(kEntityPlayer, 73);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 96);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 11
+IMPLEMENT_ACTION(setItemLocation)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (entry->isPresent)
+ return kSceneInvalid;
+
+ entry->location = (ObjectLocation)hotspot.param2;
+
+ if (item == kItemCorpse) {
+ ObjectLocation corpseLocation = getInventory()->get(kItemCorpse)->location;
+ getProgress().eventCorpseMovedFromFloor = (corpseLocation == kObjectLocation3 || corpseLocation == kObjectLocation4);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 12
+IMPLEMENT_ACTION(knockNoSound)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+ if (object >= kObjectMax)
+ return kSceneInvalid;
+
+ if (getObjects()->get(object).entity)
+ getSavePoints()->push(kEntityPlayer, getObjects()->get(object).entity, kActionKnock, object);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 13
+IMPLEMENT_ACTION(pickItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == 0);
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (!entry->location)
+ return kSceneInvalid;
+
+ // Special case for corpse
+ if (item == kItemCorpse) {
+ pickCorpse(location, process);
+ return kSceneInvalid;
+ }
+
+ // Add and process items
+ getInventory()->addItem(item);
+
+ switch (item) {
+ default:
+ break;
+
+ case kItemGreenJacket:
+ pickGreenJacket(process);
+ break;
+
+ case kItemScarf:
+ pickScarf(process);
+
+ // stop processing
+ return kSceneInvalid;
+
+ case kItemParchemin:
+ if (location != kObjectLocation2)
+ break;
+
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ break;
+
+ case kItemBomb:
+ RESET_ENTITY_STATE(kEntityAbbot, Abbot, setup_pickBomb);
+ break;
+
+ case kItemBriefcase:
+ getSound()->playSoundEvent(kEntityPlayer, 83);
+ break;
+ }
+
+ // Load item scene
+ if (getInventory()->get(item)->scene) {
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = (hotspot.scene ? hotspot.scene : getState()->scene);
+ }
+
+ getScenes()->loadScene(getInventory()->get(item)->scene);
+
+ // do not process further
+ sceneIndex = kSceneNone;
+ }
+
+ // Select item
+ if (getInventory()->get(item)->isSelectable) {
+ getInventory()->selectItem(item);
+ _engine->getCursor()->setStyle(getInventory()->get(item)->cursor);
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 14
+IMPLEMENT_ACTION(dropItem)
+ InventoryItem item = (InventoryItem)hotspot.param1;
+ ObjectLocation location = (ObjectLocation)hotspot.param2;
+ bool process = (hotspot.scene == kSceneNone);
+
+ if (item >= kPortraitOriginal)
+ return kSceneInvalid;
+
+ if (!getInventory()->hasItem(item))
+ return kSceneInvalid;
+
+ if (location < kObjectLocation1)
+ return kSceneInvalid;
+
+ // Handle actions
+ if (item == kItemBriefcase) {
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+
+ if (location == kObjectLocation2) {
+ if (!getProgress().field_58) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_58 = 1;
+ }
+
+ if (getInventory()->get(kItemParchemin)->location == kObjectLocation2) {
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getSound()->playSoundEvent(kEntityPlayer, 9);
+ }
+ }
+ }
+
+ // Update item location
+ getInventory()->removeItem(item, location);
+
+ if (item == kItemCorpse)
+ dropCorpse(process);
+
+ // Unselect item
+ getInventory()->unselectItem();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 15: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 16
+IMPLEMENT_ACTION(enterCompartment)
+ if (getObjects()->get(kObjectCompartment1).location == kObjectLocation1 || getObjects()->get(kObjectCompartment1).location == kObjectLocation3 || getInventory()->getSelectedItem() == kItemKey)
+ return action_compartment(hotspot);
+
+ if (getProgress().eventCorpseFound) {
+ if (hotspot.action != SceneHotspot::kActionEnterCompartment || getInventory()->get(kItemBriefcase)->location != kObjectLocation2)
+ return action_compartment(hotspot);
+
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 22);
+
+ if (getProgress().field_78 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_78 = 0;
+ }
+
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 77);
+
+ return kSceneNone;
+ }
+
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getSound()->playSound(kEntityPlayer, "LIB014");
+ playAnimation(kEventCathFindCorpse);
+ getSound()->playSound(kEntityPlayer, "LIB015");
+ getProgress().eventCorpseFound = true;
+
+ return kSceneCompartmentCorpse;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 17: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 18
+IMPLEMENT_ACTION(getOutsideTrain)
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowNight) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (!getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == kObjectLocation2))
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase) {
+
+ switch (object) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideTylerCompartmentNight : kEventCathGoOutsideTylerCompartmentDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGoOutsideNight : kEventCathGoOutsideDay);
+ getProgress().field_C8 = 1;
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ getEvent(kEventCathLookOutsideWindowDay) = 1;
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ break;
+ }
+ } else {
+ if (object == kObjectOutsideTylerCompartment || object == kObjectOutsideBetweenCompartments || object == kObjectOutsideAnnaCompartment) {
+ playAnimation(isNight() ? kEventCathLookOutsideWindowNight : kEventCathLookOutsideWindowDay);
+ getScenes()->processScene();
+ return kSceneNone;
+ }
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 19
+IMPLEMENT_ACTION(slip)
+ switch((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathSlipTylerCompartmentNight : kEventCathSlipTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathSlipNight : kEventCathSlipDay);
+ break;
+ }
+
+ getProgress().field_C8 = 0;
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 20
+IMPLEMENT_ACTION(getInsideTrain)
+ switch ((ObjectIndex)hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case kObjectOutsideTylerCompartment:
+ playAnimation(isNight() ? kEventCathGetInsideTylerCompartmentNight : kEventCathGetInsideTylerCompartmentDay);
+ break;
+
+ case kObjectOutsideBetweenCompartments:
+ playAnimation(isNight() ? kEventCathGetInsideNight : kEventCathGetInsideDay);
+ break;
+
+ case kObjectOutsideAnnaCompartment:
+ playAnimation(kEventCathGettingInsideAnnaCompartment);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 21
+IMPLEMENT_ACTION(climbUpTrain)
+ byte action = hotspot.param1;
+
+ if (action != 1 && action != 2)
+ return kSceneInvalid;
+
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ if (action == 2)
+ playAnimation(kEventCathClimbUpTrainGreenJacket);
+
+ playAnimation(kEventCathTopTrainGreenJacket);
+ break;
+
+ case kChapter5:
+ if (action == 2)
+ playAnimation(getProgress().isNightTime ? kEventCathClimbUpTrainNoJacketNight : kEventCathClimbUpTrainNoJacketDay);
+
+ playAnimation(getProgress().isNightTime ? kEventCathTopTrainNoJacketNight : kEventCathTopTrainNoJacketDay);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 22
+IMPLEMENT_ACTION(climbDownTrain)
+ EventIndex evt = kEventNone;
+ switch (getProgress().chapter) {
+ default:
+ return kSceneInvalid;
+
+ case kChapter2:
+ case kChapter3:
+ evt = kEventCathClimbDownTrainGreenJacket;
+ break;
+
+ case kChapter5:
+ evt = (getProgress().isNightTime ? kEventCathClimbDownTrainNoJacketNight : kEventCathClimbDownTrainNoJacketDay);
+ break;
+ }
+
+ playAnimation(evt);
+ if (evt == kEventCathClimbDownTrainNoJacketDay)
+ getSound()->playSoundEvent(kEntityPlayer, 37);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 23
+IMPLEMENT_ACTION(jumpUpDownTrain)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionBreakCeiling);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionJumpDownCeiling);
+ break;
+
+ case 3:
+ if (getInventory()->getSelectedItem() == kItemBriefcase) {
+ getInventory()->removeItem(kItemBriefcase, kObjectLocation3);
+ getSound()->playSoundEvent(kEntityPlayer, 82);
+ getInventory()->unselectItem();
+ }
+
+ // Show animation with or without briefcase
+ playAnimation((getInventory()->get(kItemBriefcase)->location - 3) ? kEventCathJumpUpCeilingBriefcase : kEventCathJumpUpCeiling);
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ break;
+
+ case 4:
+ if (getProgress().chapter == kChapter1)
+ getSavePoints()->push(kEntityPlayer, kEntityKronos, kAction202621266);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 24
+IMPLEMENT_ACTION(unbound)
+ byte action = hotspot.param1;
+
+ switch (action) {
+ default:
+ break;
+
+ case 1:
+ playAnimation(kEventCathStruggleWithBonds);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 2:
+ playAnimation(kEventCathBurnRope);
+ if (hotspot.scene)
+ getScenes()->processScene();
+ break;
+
+ case 3:
+ if (getEvent(kEventCathBurnRope)) {
+ playAnimation(kEventCathRemoveBonds);
+ getProgress().field_84 = 0;
+ getScenes()->loadSceneFromPosition(kCarBaggageRear, 89);
+ return kSceneNone;
+ }
+ break;
+
+ case 4:
+ if (!getEvent(kEventCathStruggleWithBonds2)) {
+ playAnimation(kEventCathStruggleWithBonds2);
+ getSound()->playSoundEvent(kEntityPlayer, 101);
+ getInventory()->setLocationAndProcess(kItemMatch, kObjectLocation2);
+ if (!hotspot.scene)
+ getScenes()->processScene();
+ }
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityIvo, kAction192637492);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 25
+IMPLEMENT_ACTION(25)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction272177921);
+ break;
+
+ case 2:
+ if (!getSound()->isBuffered("MUS021"))
+ getSound()->playSound(kEntityPlayer, "MUS021", SoundManager::kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (!getInventory()->hasItem(kItemKey) && !getEvent(kEventAnnaBaggageArgument)) {
+ RESET_ENTITY_STATE(kEntityAnna, Anna, setup_baggage);
+ return kSceneNone;
+ }
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 26
+IMPLEMENT_ACTION(26)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction158610240);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225367984);
+ getInventory()->unselectItem();
+ return kSceneNone;
+
+ case 3:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction191001984);
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction201959744);
+ return kSceneNone;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction169300225);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 27
+IMPLEMENT_ACTION(27)
+ if (!getSound()->isBuffered("LIB031", true))
+ getSound()->playSoundEvent(kEntityPlayer, 31);
+
+ switch (getEntityData(kEntityPlayer)->car) {
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction225358684, hotspot.param1);
+ break;
+
+ case kCarRedSleeping:
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction225358684, hotspot.param1);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 28
+IMPLEMENT_ACTION(concertSitCough)
+ switch(hotspot.param1) {
+ default:
+ return kSceneInvalid;
+
+ case 1:
+ playAnimation(kEventConcertSit);
+ break;
+
+ case 2:
+ playAnimation(kEventConcertCough);
+ break;
+ }
+
+ if (!hotspot.scene)
+ getScenes()->processScene();
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 29
+IMPLEMENT_ACTION(29)
+ getProgress().field_C = 1;
+ getSound()->playSoundEvent(kEntityPlayer, hotspot.param1, hotspot.param2);
+
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param3);
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 30
+IMPLEMENT_ACTION(catchBeetle)
+ if (!getBeetle()->isLoaded())
+ return kSceneInvalid;
+
+ if (getBeetle()->catchBeetle()) {
+ getBeetle()->unload();
+ getInventory()->get(kItemBeetle)->location = kObjectLocation1;
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionCatchBeetle);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 31
+IMPLEMENT_ACTION(exitCompartment)
+ if (!getProgress().field_30 && getProgress().jacket != kJacketOriginal) {
+ getSaveLoad()->saveGame(kSavegameTypeTime, kEntityPlayer, kTimeNone);
+ getProgress().field_30 = 1;
+ }
+
+ getObjects()->updateLocation2(kObjectCompartment1, (ObjectLocation)hotspot.param2);
+
+ // fall to case enterCompartment action
+ return action_enterCompartment(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 32
+IMPLEMENT_ACTION(32)
+ switch(hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntitySalko, kAction167992577);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction202884544);
+ break;
+
+ case 3:
+ if (getProgress().chapter == kChapter5) {
+ getSavePoints()->push(kEntityPlayer, kEntityAbbot, kAction168646401);
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction168646401);
+ } else {
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction203339360);
+ }
+ // Stop processing further scenes
+ return kSceneNone;
+
+ case 4:
+ getSavePoints()->push(kEntityPlayer, kEntityMilos, kAction169773228);
+ break;
+
+ case 5:
+ getSavePoints()->push(kEntityPlayer, kEntityVesna, kAction167992577);
+ break;
+
+ case 6:
+ getSavePoints()->push(kEntityPlayer, kEntityAugust, kAction203078272);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 33
+IMPLEMENT_ACTION(useWhistle)
+ EventIndex evt = kEventNone;
+ SceneIndex sceneIndex = kSceneInvalid;
+
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction205294778);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) {
+ evt = kEventCathOpenEgg;
+
+ Scene *scene = getScenes()->get(hotspot.scene);
+ if (scene->getHotspot())
+ sceneIndex = scene->getHotspot()->scene;
+
+ } else {
+ evt = kEventCathOpenEggNoBackground;
+ }
+ getProgress().isEggOpen = true;
+ break;
+
+ case 2:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kAction224309120);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathCloseEgg : kEventCathCloseEggNoBackground;
+ getProgress().isEggOpen = false;
+ break;
+
+ case 3:
+ if (getEvent(kEventKronosBringFirebird)) {
+ getSavePoints()->push(kEntityPlayer, kEntityAnna, kActionUseWhistle);
+ break;
+ }
+
+ evt = (getEntities()->isInsideCompartment(kEntityPlayer, kCarGreenSleeping, kPosition_8200)) ? kEventCathUseWhistleOpenEgg : kEventCathUseWhistleOpenEggNoBackground;
+ break;
+
+ }
+
+ if (evt != kEventNone) {
+ playAnimation(evt);
+ if (sceneIndex == kSceneNone || !hotspot.scene)
+ getScenes()->processScene();
+ }
+
+ return sceneIndex;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 34
+IMPLEMENT_ACTION(openMatchBox)
+ // If the match is already in the inventory, do nothing
+ if (!getInventory()->get(kItemMatch)->location
+ || getInventory()->get(kItemMatch)->isPresent)
+ return kSceneInvalid;
+
+ getInventory()->addItem(kItemMatch);
+ getSound()->playSoundEvent(kEntityPlayer, 102);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 35
+IMPLEMENT_ACTION(openBed)
+ getSound()->playSoundEvent(kEntityPlayer, 59);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 36: Dummy action
+
+//////////////////////////////////////////////////////////////////////////
+// Action 37
+IMPLEMENT_ACTION(dialog)
+ getSound()->playDialog(kEntityTables4, (EntityIndex)hotspot.param1, SoundManager::kFlagDefault, 0);
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 38
+IMPLEMENT_ACTION(eggBox)
+ getSound()->playSoundEvent(kEntityPlayer, 43);
+ if (getProgress().field_7C && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_7C = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 39
+IMPLEMENT_ACTION(39)
+ getSound()->playSoundEvent(kEntityPlayer, 24);
+ if (getProgress().field_80 && !getSound()->isBuffered("MUS003")) {
+ getSound()->playSound(kEntityPlayer, "MUS003", SoundManager::kFlagDefault);
+ getProgress().field_80 = 0;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 40
+IMPLEMENT_ACTION(bed)
+ getSound()->playSoundEvent(kEntityPlayer, 85);
+ // falls to case knockNoSound
+ return action_knockNoSound(hotspot);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 41
+IMPLEMENT_ACTION(playMusicChapter)
+ byte id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = hotspot.param1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = hotspot.param2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = hotspot.param3;
+ break;
+ }
+
+ if (id) {
+ Common::String filename = Common::String::printf("MUS%03d", id);
+
+ if (!getSound()->isBuffered(filename))
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 42
+IMPLEMENT_ACTION(playMusicChapterSetupTrain)
+ int id = 0;
+ switch (getProgress().chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ id = 1;
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ id = 2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ id = 4;
+ break;
+ }
+
+ Common::String filename = Common::String::printf("MUS%03d", hotspot.param1);
+
+ if (!getSound()->isBuffered(filename) && hotspot.param3 & id) {
+ getSound()->playSound(kEntityPlayer, filename, SoundManager::kFlagDefault);
+
+ getSavePoints()->call(kEntityPlayer, kEntityTrain, kAction203863200, filename.c_str());
+ getSavePoints()->push(kEntityPlayer, kEntityTrain, kAction222746496, hotspot.param2);
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// // Action 43
+IMPLEMENT_ACTION(switchChapter)
+ // Nothing to do here as an hotspot action
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Action 44
+IMPLEMENT_ACTION(44)
+ switch (hotspot.param1) {
+ default:
+ break;
+
+ case 1:
+ getSavePoints()->push(kEntityPlayer, kEntityRebecca, kAction205034665);
+ break;
+
+ case 2:
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kAction225358684);
+ break;
+ }
+
+ return kSceneInvalid;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helper functions
+//////////////////////////////////////////////////////////////////////////
+void Action::pickGreenJacket(bool process) const {
+ getProgress().jacket = kJacketGreen;
+ getInventory()->addItem(kItemMatchBox);
+
+ getObjects()->update(kObjectOutsideTylerCompartment, kEntityPlayer, kObjectLocation2, kCursorKeepValue, kCursorKeepValue);
+ playAnimation(kEventPickGreenJacket);
+
+ getInventory()->setPortrait(kPortraitGreen);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickScarf(bool process) const {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventPickScarfGreen : kEventPickScarfOriginal);
+
+ if (process)
+ getScenes()->processScene();
+}
+
+void Action::pickCorpse(ObjectLocation bedPosition, bool process) const {
+
+ if (getProgress().jacket == kJacketOriginal)
+ getProgress().jacket = kJacketBlood;
+
+ switch(getInventory()->get(kItemCorpse)->location) {
+ // No way to pick the corpse
+ default:
+ break;
+
+ // Floor
+ case kObjectLocation1:
+ // Bed is fully opened, move corpse directly there
+ if (bedPosition == 4) {
+ playAnimation(kEventCorpsePickFloorOpenedBedOriginal);
+
+ getInventory()->get(kItemCorpse)->location = kObjectLocation5;
+ break;
+ }
+
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickFloorGreen : kEventCorpsePickFloorOriginal);
+ break;
+
+ // Bed
+ case kObjectLocation2:
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpsePickBedGreen : kEventCorpsePickBedOriginal);
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+
+ // Add corpse to inventory
+ if (bedPosition != 4) { // bed is not fully opened
+ getInventory()->addItem(kItemCorpse);
+ getInventory()->selectItem(kItemCorpse);
+ _engine->getCursor()->setStyle(kCursorCorpse);
+ }
+}
+
+void Action::dropCorpse(bool process) const {
+ switch(getInventory()->get(kItemCorpse)->location) {
+ default:
+ break;
+
+ case kObjectLocation1: // Floor
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropFloorGreen : kEventCorpseDropFloorOriginal);
+ break;
+
+ case kObjectLocation2: // Bed
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropBedGreen : kEventCorpseDropBedOriginal);
+ break;
+
+ case kObjectLocation4: // Window
+ // Say goodbye to an old friend
+ getInventory()->get(kItemCorpse)->location = kObjectLocationNone;
+ getProgress().eventCorpseThrown = true;
+
+ if (getState()->time <= kTime1138500) {
+ playAnimation(getProgress().jacket == kJacketGreen ? kEventCorpseDropWindowGreen : kEventCorpseDropWindowOriginal);
+
+ getProgress().field_24 = true;
+ } else {
+ playAnimation(kEventCorpseDropBridge);
+ }
+
+ getProgress().eventCorpseMovedFromFloor = true;
+ break;
+ }
+
+ if (process)
+ getScenes()->processScene();
+}
+
+bool Action::handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const {
+#define ENTITY_PARAMS(entity, index, id) \
+ ((EntityData::EntityParametersIIII*)getEntities()->get(entity)->getParamData()->getParameters(8, index))->param##id
+
+ // Only handle compartments
+ if (getEntityData(kEntityPlayer)->location != kLocationOutsideCompartment
+ || ((object < kObjectCompartment2 || object > kObjectCompartment8) && (object < kObjectCompartmentA || object > kObjectCompartmentH)))
+ return false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Gendarmes
+ if (getEntityData(kEntityPlayer)->car == getEntityData(kEntityGendarmes)->car
+ && getEntityData(kEntityGendarmes)->location == kLocationOutsideCompartment
+ && !getEntities()->compare(kEntityPlayer, kEntityGendarmes)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping
+ && getEntityData(kEntityMertens)->car == kCarGreenSleeping
+ && !getEntityData(kEntityMertens)->location
+ && !ENTITY_PARAMS(kEntityMertens, 0, 1)) {
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityMertens)) {
+
+ if (getEntityData(kEntityMertens)->entityPosition < kPosition_2740
+ && getEntityData(kEntityMertens)->entityPosition > kPosition_850
+ && (getEntityData(kEntityCoudert)->car != kCarGreenSleeping || getEntityData(kEntityCoudert)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarGreenSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playWarningCompartment(kEntityMertens, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionUp
+ && getEntityData(kEntityMertens)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+ }
+
+ if (getEntityData(kEntityMertens)->direction == kDirectionDown
+ && getEntityData(kEntityMertens)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityMertens))
+ getSound()->playSound(kEntityMertens, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ if (getEntityData(kEntityPlayer)->car != kCarRedSleeping
+ || !getEntityData(kEntityCoudert)->car
+ || getEntityData(kEntityCoudert)->location != kLocationOutsideCompartment
+ || ENTITY_PARAMS(kEntityCoudert, 0, 1))
+ return false;
+
+ if (!getEntities()->compare(kEntityPlayer, kEntityCoudert)) {
+
+ if (getEntityData(kEntityCoudert)->entityPosition < kPosition_2740
+ && getEntityData(kEntityCoudert)->entityPosition > kPosition_850
+ && (getEntityData(kEntityMertens)->car != kCarRedSleeping || getEntityData(kEntityMertens)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityVerges)->car != kCarRedSleeping || getEntityData(kEntityVerges)->entityPosition > kPosition_2740)
+ && (getEntityData(kEntityMmeBoutarel)->car != kCarRedSleeping || getEntityData(kEntityMmeBoutarel)->entityPosition > kPosition_2740)) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playWarningCompartment(kEntityCoudert, object);
+
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction305159806);
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = Up
+ if (getEntityData(kEntityCoudert)->direction == kDirectionUp
+ && getEntityData(kEntityCoudert)->entityPosition < getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object);
+
+ return true;
+ }
+
+ // Direction = down
+ if (getEntityData(kEntityCoudert)->direction == kDirectionDown
+ && getEntityData(kEntityCoudert)->entityPosition > getEntityData(kEntityPlayer)->entityPosition) {
+ if (doPlaySound)
+ playCompartmentSoundEvents(object);
+
+ if (!getSound()->isBuffered(kEntityCoudert))
+ getSound()->playSound(kEntityCoudert, (rnd(2)) ? "JAC1000" : "JAC1000A");
+
+ if (doLoadScene)
+ getScenes()->loadSceneFromObject(object, true);
+ }
+ }
+
+ return false;
+}
+
+void Action::playCompartmentSoundEvents(ObjectIndex object) const {
+ if (getObjects()->get(object).location == kObjectLocation1 || getObjects()->get(object).location == kObjectLocation3 || getEntities()->checkFields2(object)) {
+ getSound()->playSoundEvent(kEntityPlayer, 13);
+ } else {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->playSoundEvent(kEntityPlayer, 15, 3);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cursors
+//////////////////////////////////////////////////////////////////////////
+CursorStyle Action::getCursor(const SceneHotspot &hotspot) const {
+ // Simple cursor style
+ if (hotspot.cursor != kCursorProcess)
+ return (CursorStyle)hotspot.cursor;
+
+ ObjectIndex object = (ObjectIndex)hotspot.param1;
+
+ switch (hotspot.action) {
+ default:
+ return kCursorNormal;
+
+ case SceneHotspot::kActionInventory:
+ if (!getState()->sceneBackup2 && (getEvent(kEventKronosBringFirebird) || getProgress().isEggOpen))
+ return kCursorNormal;
+ else
+ return kCursorBackward;
+
+ case SceneHotspot::kActionKnockOnDoor:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+ else
+ return (CursorStyle)getObjects()->get(object).cursor;
+
+ case SceneHotspot::kAction12:
+ warning("================================= OBJECT %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getObjects()->get(object).entity)
+ return (CursorStyle)getObjects()->get(object).cursor;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionPickItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if ((!getInventory()->getSelectedItem() || getInventory()->getSelectedEntry()->manualSelect)
+ && (object != kObject21 || getProgress().eventCorpseMovedFromFloor))
+ return kCursorHand;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDropItem:
+ warning("================================= ITEM %03d =================================", object);
+ if (object >= kObjectCompartmentA)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != (InventoryItem)object)
+ return kCursorNormal;
+
+ if (object == kObject20 && hotspot.param2 == 4 && !getProgress().isTrainRunning)
+ return kCursorNormal;
+
+ if (object == kObjectHandleInsideBathroom && hotspot.param2 == 1 && getProgress().field_5C)
+ return kCursorNormal;
+
+ return (CursorStyle)getInventory()->getSelectedEntry()->cursor;
+
+ case SceneHotspot::kAction15:
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getProgress().isEqual(object, hotspot.param2))
+ return (CursorStyle)hotspot.param3;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionEnterCompartment:
+ if ((getInventory()->getSelectedItem() != kItemKey || getObjects()->get(kObjectCompartment1).location)
+ && (getObjects()->get(kObjectCompartment1).location != 1 || !getInventory()->hasItem(kItemKey)
+ || (getInventory()->getSelectedItem() != kItemFirebird && getInventory()->getSelectedItem() != kItemBriefcase)))
+ goto LABEL_KEY;
+
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor; // TODO is that always the same as kCursorKey ?
+
+ case SceneHotspot::kActionGetOutsideTrain:
+ if (getProgress().jacket != kJacketGreen)
+ return kCursorNormal;
+
+ if ((getEvent(kEventCathLookOutsideWindowDay) || getEvent(kEventCathLookOutsideWindowDay) || getObjects()->get(kObjectCompartment1).location2 == kObjectLocation1)
+ && getProgress().isTrainRunning
+ && (object != kObjectOutsideAnnaCompartment || (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840) && getObjects()->get(kObjectOutsideBetweenCompartments).location == 2))
+ && getInventory()->getSelectedItem() != kItemBriefcase && getInventory()->getSelectedItem() != kItemFirebird)
+ return kCursorForward;
+
+ return (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation2) ? kCursorNormal : kCursorMagnifier;
+
+ case SceneHotspot::kActionSlip:
+ return (getProgress().field_C8 < 1) ? kCursorNormal : kCursorLeft;
+
+ case SceneHotspot::kActionClimbUpTrain:
+ if (getProgress().isTrainRunning
+ && (getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3 || getProgress().chapter == kChapter5)
+ && getInventory()->getSelectedItem() != kItemFirebird
+ && getInventory()->getSelectedItem() != kItemBriefcase)
+ return kCursorUp;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionJumpUpDownTrain:
+ if (object != kObjectCompartment1)
+ return kCursorNormal;
+
+ return (getObjects()->get(kObjectCeiling).location < kObjectLocation1) ? kCursorHand : kCursorNormal;
+
+ case SceneHotspot::kActionUnbound:
+ if (hotspot.param2 != 2)
+ return kCursorNormal;
+
+ if (getEvent(kEventCathBurnRope) || !getEvent(kEventCathStruggleWithBonds2))
+ return kCursorNormal;
+
+ return kCursorHand;
+
+ case SceneHotspot::kActionCatchBeetle:
+ if (!getBeetle()->isLoaded())
+ return kCursorNormal;
+
+ if (!getBeetle()->isCatchable())
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox && getInventory()->hasItem(kItemMatch))
+ return (CursorStyle)getInventory()->get(kItemMatchBox)->cursor;
+
+ return kCursorHandPointer;
+
+ case SceneHotspot::KActionUseWhistle:
+ if (object != kObjectCompartment3)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() == kItemWhistle)
+ return kCursorWhistle;
+ else
+ return kCursorNormal;
+
+ case SceneHotspot::kActionOpenBed:
+ if (getProgress().chapter < kChapter2)
+ return kCursorHand;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionDialog:
+ if (getSound()->getDialogName((EntityIndex)object))
+ return kCursorHandPointer;
+
+ return kCursorNormal;
+
+ case SceneHotspot::kActionBed:
+ if (getProgress().field_18 == 2 && !getProgress().field_E4
+ && (getState()->time > kTimeBedTime
+ || (getProgress().eventMetAugust && getProgress().field_CC
+ && (!getProgress().field_24 || getProgress().field_3C))))
+ return kCursorSleep;
+
+ return kCursorNormal;
+
+LABEL_KEY:
+ // Handle compartment action
+ case SceneHotspot::kActionCompartment:
+ case SceneHotspot::kActionExitCompartment:
+ warning("================================= DOOR %03d =================================", object);
+ if (object >= kObjectMax)
+ return kCursorNormal;
+
+ if (getInventory()->getSelectedItem() != kItemKey
+ || getObjects()->get(object).entity
+ || getObjects()->get(object).location != 1
+ || !getObjects()->get(object).cursor2
+ || getEntities()->isInsideCompartments(kEntityPlayer)
+ || getEntities()->checkFields2(object))
+ return (CursorStyle)getObjects()->get(object).cursor2;
+ else
+ return (CursorStyle)getInventory()->get(kItemKey)->cursor;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Animation
+//////////////////////////////////////////////////////////////////////////
+
+// Play an animation and add delta time to global game time
+void Action::playAnimation(EventIndex index, bool debugMode) const {
+ if (index >= _animationListSize)
+ error("Action::playAnimation: invalid event index (value=%i, max=%i)", index, _animationListSize);
+
+ // In debug mode, just show the animation
+ if (debugMode) {
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis")))
+ animation.play();
+ return;
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Hide cursor
+ _engine->getCursor()->show(false);
+
+ // Show inventory & hourglass
+ getInventory()->show();
+ getInventory()->showHourGlass();
+
+ if (!getFlags()->mouseRightClick) {
+
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER")) {
+ getSound()->processEntry("TIMER");
+ setGlobalTimer(105);
+ }
+ }
+
+ bool processSound = false;
+ if (index >= kEventCorpseDropFloorOriginal
+ || index == kEventCathWakingUp
+ || index == kEventConcertCough
+ || index == kEventConcertSit
+ || index == kEventConcertLeaveWithBriefcase)
+ processSound = true;
+
+ Animation animation;
+ if (animation.load(getArchive(Common::String(_animationList[index].filename) + ".nis") , processSound ? Animation::kFlagDefault : Animation::kFlagProcess))
+ animation.play();
+
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+ }
+
+ // Show cursor
+ _engine->getCursor()->show(true);
+
+ getEvent(index) = 1;
+
+ // Adjust game time
+ getState()->timeTicks += _animationList[index].time;
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)(_animationList[index].time * getState()->timeDelta));
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/action.h b/engines/lastexpress/game/action.h
new file mode 100644
index 0000000000..6a2e3eb597
--- /dev/null
+++ b/engines/lastexpress/game/action.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 LASTEXPRESS_ACTION_H
+#define LASTEXPRESS_ACTION_H
+
+#include "lastexpress/shared.h"
+
+#include "common/array.h"
+#include "common/func.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define DECLARE_ACTION(name) \
+ SceneIndex action_##name(const SceneHotspot &hotspot) const
+
+#define ADD_ACTION(name) \
+ _actions.push_back(new Functor1MemConst<const SceneHotspot &, SceneIndex, Action>(this, &Action::action_##name));
+
+#define IMPLEMENT_ACTION(name) \
+ SceneIndex Action::action_##name(const SceneHotspot &hotspot) const { \
+ debugC(6, kLastExpressDebugLogic, "Hotspot action: " #name "%s", hotspot.toString().c_str());
+
+class LastExpressEngine;
+class SceneHotspot;
+
+class Action {
+public:
+ Action(LastExpressEngine *engine);
+ ~Action();
+
+ // Hotspot action
+ SceneIndex processHotspot(const SceneHotspot &hotspot);
+
+ // Cursor
+ CursorStyle getCursor(const SceneHotspot &hotspot) const;
+
+ // Animation
+ void playAnimation(EventIndex index, bool debugMode = false) const;
+
+ // Compartment action
+ bool handleOtherCompartment(ObjectIndex object, bool doPlaySound, bool doLoadScene) const;
+
+private:
+ typedef Common::Functor1<const SceneHotspot &, SceneIndex> ActionFunctor;
+
+ LastExpressEngine *_engine;
+ Common::Array<ActionFunctor *> _actions;
+
+ // Each action is of the form action_<name>(SceneHotspot *hotspot)
+ // - a pointer to each action is added to the _actions array
+ // - processHotspot simply calls the proper function given by the hotspot->action value
+ //
+ // Note: even though there are 44 actions, only 41 are used in processHotspot
+
+ DECLARE_ACTION(inventory);
+ DECLARE_ACTION(savePoint);
+ DECLARE_ACTION(playSound);
+ DECLARE_ACTION(playMusic);
+ DECLARE_ACTION(knock);
+ DECLARE_ACTION(compartment);
+ DECLARE_ACTION(playSounds);
+ DECLARE_ACTION(playAnimation);
+ DECLARE_ACTION(openCloseObject);
+ DECLARE_ACTION(updateObjetLocation2);
+ DECLARE_ACTION(setItemLocation);
+ DECLARE_ACTION(knockNoSound);
+ DECLARE_ACTION(pickItem);
+ DECLARE_ACTION(dropItem);
+ DECLARE_ACTION(enterCompartment);
+ DECLARE_ACTION(getOutsideTrain);
+ DECLARE_ACTION(slip);
+ DECLARE_ACTION(getInsideTrain);
+ DECLARE_ACTION(climbUpTrain);
+ DECLARE_ACTION(climbDownTrain);
+ DECLARE_ACTION(jumpUpDownTrain);
+ DECLARE_ACTION(unbound);
+ DECLARE_ACTION(25);
+ DECLARE_ACTION(26);
+ DECLARE_ACTION(27);
+ DECLARE_ACTION(concertSitCough);
+ DECLARE_ACTION(29);
+ DECLARE_ACTION(catchBeetle);
+ DECLARE_ACTION(exitCompartment);
+ DECLARE_ACTION(32);
+ DECLARE_ACTION(useWhistle);
+ DECLARE_ACTION(openMatchBox);
+ DECLARE_ACTION(openBed);
+ DECLARE_ACTION(dialog);
+ DECLARE_ACTION(eggBox);
+ DECLARE_ACTION(39);
+ DECLARE_ACTION(bed);
+ DECLARE_ACTION(playMusicChapter);
+ DECLARE_ACTION(playMusicChapterSetupTrain);
+ DECLARE_ACTION(switchChapter);
+ DECLARE_ACTION(44);
+
+ // Special dummy function
+ DECLARE_ACTION(dummy);
+
+ // Helpers
+ void pickGreenJacket(bool process) const;
+ void pickScarf(bool process) const;
+ void pickCorpse(ObjectLocation bedPosition, bool process) const;
+ void dropCorpse(bool process) const;
+
+ void playCompartmentSoundEvents(ObjectIndex object) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ACTION_H
diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp
new file mode 100644
index 0000000000..665edb79a5
--- /dev/null
+++ b/engines/lastexpress/game/beetle.cpp
@@ -0,0 +1,517 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/beetle.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+Beetle::Beetle(LastExpressEngine *engine) : _engine(engine), _data(NULL) {}
+
+Beetle::~Beetle() {
+ SAFE_DELETE(_data);
+
+ // Free passed pointers
+ _engine = NULL;
+}
+
+void Beetle::load() {
+ // Only load in chapter 2 & 3
+ if (getProgress().chapter != kChapter2 && getProgress().chapter != kChapter3)
+ return;
+
+ // Already loaded
+ if (_data)
+ return;
+
+ // Do not load if beetle is in the wrong location
+ if (getInventory()->get(kItemBeetle)->location != kObjectLocation3)
+ return;
+
+ ///////////////////////
+ // Load Beetle data
+ _data = new BeetleData();
+
+ // Load sequences
+ _data->sequences.push_back(loadSequence("BW000.seq")); // 0
+ _data->sequences.push_back(loadSequence("BT000045.seq"));
+ _data->sequences.push_back(loadSequence("BT045000.seq"));
+ _data->sequences.push_back(loadSequence("BW045.seq"));
+ _data->sequences.push_back(loadSequence("BT045090.seq"));
+ _data->sequences.push_back(loadSequence("BT090045.seq")); // 5
+ _data->sequences.push_back(loadSequence("BW090.seq"));
+ _data->sequences.push_back(loadSequence("BT090135.seq"));
+ _data->sequences.push_back(loadSequence("BT135090.seq"));
+ _data->sequences.push_back(loadSequence("BW135.seq"));
+ _data->sequences.push_back(loadSequence("BT135180.seq")); // 10
+ _data->sequences.push_back(loadSequence("BT180135.seq"));
+ _data->sequences.push_back(loadSequence("BW180.seq"));
+ _data->sequences.push_back(loadSequence("BT180225.seq"));
+ _data->sequences.push_back(loadSequence("BT225180.seq"));
+ _data->sequences.push_back(loadSequence("BW225.seq")); // 15
+ _data->sequences.push_back(loadSequence("BT225270.seq"));
+ _data->sequences.push_back(loadSequence("BT270225.seq"));
+ _data->sequences.push_back(loadSequence("BW270.seq"));
+ _data->sequences.push_back(loadSequence("BT270315.seq"));
+ _data->sequences.push_back(loadSequence("BT315270.seq")); // 20
+ _data->sequences.push_back(loadSequence("BW315.seq"));
+ _data->sequences.push_back(loadSequence("BT315000.seq"));
+ _data->sequences.push_back(loadSequence("BT000315.seq"));
+ _data->sequences.push_back(loadSequence("BA135.seq"));
+ _data->sequences.push_back(loadSequence("BL045.seq")); // 25
+ _data->sequences.push_back(loadSequence("BL000.seq"));
+ _data->sequences.push_back(loadSequence("BL315.seq"));
+ _data->sequences.push_back(loadSequence("BL180.seq"));
+
+ // Init fields
+ _data->field_74 = 0;
+
+ // Check that all sequences are loaded properly
+ _data->isLoaded = true;
+ for (int i = 0; i < (int)_data->sequences.size(); i++) {
+ if (!_data->sequences[i]->isLoaded()) {
+ _data->isLoaded = false;
+ break;
+ }
+ }
+
+ _data->field_D9 = 10;
+ _data->coordOffset = 5;
+ _data->coordY = 178;
+ _data->currentSequence = 0;
+ _data->offset = 0;
+ _data->frame = NULL;
+ _data->field_D5 = 0;
+ _data->indexes[0] = 29;
+ _data->field_DD = 0;
+}
+
+void Beetle::unload() {
+ // Remove sequences from display list
+ if (_data)
+ getScenes()->removeFromQueue(_data->frame);
+
+ // Delete all loaded sequences
+ SAFE_DELETE(_data);
+}
+
+bool Beetle::isLoaded() const {
+ if (!_data)
+ return false;
+
+ return _data->isLoaded;
+}
+
+bool Beetle::catchBeetle() {
+ if (!_data)
+ error("Beetle::catchBeetle: sequences have not been loaded!");
+
+ if (getInventory()->getSelectedItem() == kItemMatchBox
+ && getInventory()->hasItem(kItemMatch)
+ && ABS((int16)(getCoords().x - _data->coordX)) < 10
+ && ABS((int16)(getCoords().y - _data->coordY)) < 10) {
+ return true;
+ }
+
+ _data->field_D5 = 0;
+ move();
+
+ return false;
+}
+
+bool Beetle::isCatchable() const {
+ if (!_data)
+ error("Beetle::isCatchable: sequences have not been loaded!");
+
+ return (_data->indexes[_data->offset] >= 30);
+}
+
+void Beetle::update() {
+ if (!_data)
+ error("Beetle::update: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ move();
+
+ if (_data->field_D5)
+ _data->field_D5--;
+
+ if (_data->currentSequence && _data->indexes[_data->offset] != 29) {
+ drawUpdate();
+ return;
+ }
+
+ if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if ((!_data->field_DD && rnd(10) < 1)
+ || (_data->field_DD && rnd(30) < 1)
+ || rnd(100) < 1) {
+
+ _data->field_DD++;
+ if (_data->field_DD > 3)
+ _data->field_DD = 0;
+
+ updateData(24);
+
+ _data->coordX = (int16)(rnd(250) + 190);
+ _data->coordOffset = (int16)(rnd(5) + 5);
+
+ if (_data->field_D9 > 1)
+ _data->field_D9--;
+
+ drawUpdate();
+ }
+ }
+}
+
+void Beetle::drawUpdate() {
+ if (!_data)
+ error("Beetle::drawUpdate: sequences have not been loaded!");
+
+ if (_data->frame != NULL) {
+ getScenes()->setCoordinates(_data->frame);
+ getScenes()->removeFromQueue(_data->frame);
+ }
+
+ // Update current frame
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->currentFrame += 10;
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ _data->currentFrame++;
+ break;
+ }
+
+ // Update current sequence
+ if (_data->currentSequence->count() <= _data->currentFrame) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ _data->offset++;
+ _data->currentSequence = _data->sequences[_data->indexes[_data->offset]];
+ break;
+
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ break;
+ }
+
+ _data->currentFrame = 0;
+ if (_data->indexes[_data->offset] == 29) {
+ SAFE_DELETE(_data->frame);
+ _data->currentSequence = NULL; // pointer to existing sequence
+ return;
+ }
+ }
+
+ // Update coordinates
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 0:
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 3:
+ _data->coordX += _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+
+ case 6:
+ _data->coordX += _data->coordOffset;
+ break;
+
+ case 9:
+ _data->coordX += _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 12:
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 15:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY += _data->coordOffset;
+ break;
+
+ case 18:
+ _data->coordX -= _data->coordOffset;
+ break;
+
+ case 21:
+ _data->coordX -= _data->coordOffset;
+ _data->coordY -= _data->coordOffset;
+ break;
+ }
+
+ // Update beetle data
+ int rnd = rnd(100);
+ if (_data->coordX < 165 || _data->coordX > 465) {
+ uint index = 0;
+
+ if (rnd >= 30) {
+ if (rnd >= 70)
+ index = (_data->coordX < 165) ? 9 : 15;
+ else
+ index = (_data->coordX < 165) ? 6 : 18;
+ } else {
+ index = (_data->coordX < 165) ? 3 : 21;
+ }
+
+ updateData(index);
+ }
+
+ if (_data->coordY < 178) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ updateData(26);
+ break;
+
+ case 3:
+ updateData(25);
+ break;
+
+ case 21:
+ updateData(27);
+ break;
+ }
+ }
+
+ if (_data->coordY > 354) {
+ switch (_data->indexes[_data->offset]) {
+ default:
+ break;
+
+ case 9:
+ case 12:
+ case 15:
+ updateData(28);
+ break;
+ }
+ }
+
+#define INVERT_Y() \
+ switch (_data->indexes[_data->offset]) { \
+ default: \
+ break; \
+ case 24: \
+ case 25: \
+ case 26: \
+ case 27: \
+ case 28: \
+ _data->coordY = -_data->coordY; \
+ break; \
+ }
+
+ // Invert direction
+ INVERT_Y();
+
+ SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame);
+ updateFrame(frame);
+
+ INVERT_Y();
+
+ getScenes()->addToQueue(frame);
+
+ SAFE_DELETE(_data->frame);
+ _data->frame = frame;
+}
+
+void Beetle::move() {
+ if (!_data)
+ error("Beetle::move: sequences have not been loaded!");
+
+ if (_data->indexes[_data->offset] >= 24 && _data->indexes[_data->offset] <= 29)
+ return;
+
+ if (_data->field_D5)
+ return;
+
+ if (ABS((int)(getCoords().x - _data->coordX)) > 35)
+ return;
+
+ if (ABS((int)(getCoords().y - _data->coordY)) > 35)
+ return;
+
+ int32 deltaX = getCoords().x - _data->coordX;
+ int32 deltaY = -getCoords().y - _data->coordY;
+ uint32 index = 0;
+
+ // FIXME: check code path
+ if (deltaX >= 0) {
+ if (deltaY > 0) {
+ if (100 * deltaY - 241 * deltaX <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0)
+ index = 18;
+ else
+ index = 15;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+ }
+
+ if (deltaX < 0) {
+
+ if (deltaY > 0) {
+ if (100 * deltaY + 241 * deltaX <= 0) {
+ if (100 * deltaY + 41 * deltaX <= 0)
+ index = 6;
+ else
+ index = 9;
+ } else {
+ index = 12;
+ }
+
+ goto update_data;
+ }
+
+ if (deltaY <= 0) {
+ if (100 * deltaY - 41 * deltaX <= 0) {
+ if (100 * deltaY - 241 * deltaX <= 0)
+ index = 0;
+ else
+ index = 3;
+ } else {
+ index = 6;
+ }
+
+ goto update_data;
+ }
+ }
+
+update_data:
+ updateData(index);
+
+ if (_data->coordOffset >= 15) {
+ _data->field_D5 = 0;
+ return;
+ }
+
+ _data->coordOffset = _data->coordOffset + (int16)(4 * rnd(100)/100 + _data->field_D9);
+ _data->field_D5 = 0;
+}
+
+// Update the beetle sequence to show the correct frames in the correct place
+void Beetle::updateFrame(SequenceFrame *frame) const {
+ if (!_data)
+ error("Beetle::updateSequence: sequences have not been loaded!");
+
+ if (!frame)
+ return;
+
+ // Update coordinates
+ if (_data->coordX > 0)
+ frame->getInfo()->xPos1 = (uint16)_data->coordX;
+
+ if (_data->coordY > 0)
+ frame->getInfo()->yPos1 = (uint16)_data->coordY;
+}
+
+void Beetle::updateData(uint32 index) {
+ if (!_data)
+ error("Beetle::updateData: sequences have not been loaded!");
+
+ if (!_data->isLoaded)
+ return;
+
+ if (index == 25 || index == 26 || index == 27 || index == 28) {
+ _data->indexes[0] = index;
+ _data->indexes[1] = 29;
+ _data->offset = 0;
+
+ _data->currentSequence = _data->sequences[index];
+ _data->currentFrame = 0;
+ _data->index = index;
+ } else {
+ if (!_data->sequences[index])
+ return;
+
+ if (_data->index == index)
+ return;
+
+ _data->offset = 0;
+
+ // Special case for sequence 24
+ if (index == 24) {
+ _data->indexes[0] = index;
+ _data->coordY = 178;
+ _data->index = _data->indexes[1];
+ _data->indexes[1] = (_data->coordX >= 265) ? 15 : 9;
+ _data->currentFrame = 0;
+ _data->currentSequence = _data->sequences[index];
+ } else {
+ if (index <= _data->index) {
+ for (uint32 i = _data->index - 1; i > index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i -= 3;
+ }
+ } else {
+ for (uint32 i = _data->index + 1; i < index; ++_data->offset) {
+ _data->indexes[_data->offset] = i;
+ i += 3;
+ }
+ }
+
+ _data->index = index;
+ _data->indexes[_data->offset] = index;
+ _data->currentFrame = 0;
+ _data->offset = 0;
+ _data->currentSequence = _data->sequences[_data->indexes[0]];
+ }
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h
new file mode 100644
index 0000000000..3341e92270
--- /dev/null
+++ b/engines/lastexpress/game/beetle.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_BEETLE_H
+#define LASTEXPRESS_BEETLE_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "common/array.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Beetle {
+public:
+
+ Beetle(LastExpressEngine *engine);
+ ~Beetle();
+
+ void update();
+
+ void load();
+ void unload();
+
+ bool isLoaded() const;
+
+ bool catchBeetle();
+ bool isCatchable() const;
+
+private:
+ struct BeetleData {
+ Common::Array<Sequence *> sequences;
+
+ uint32 field_74;
+ Sequence *currentSequence;
+ uint32 currentFrame;
+ uint32 index;
+ int16 coordOffset;
+ int16 field_86;
+
+ int16 coordX;
+ int16 coordY;
+
+ uint32 indexes[16];
+
+ uint32 offset;
+ SequenceFrame *frame;
+ bool isLoaded;
+ uint32 field_D5;
+ uint32 field_D9;
+ uint32 field_DD;
+
+ BeetleData() {
+ field_74 = 0;
+ currentSequence = NULL;
+ currentFrame = 0;
+ index = 0;
+ coordOffset = 0;
+
+ field_86 = 0;
+
+ coordX = 0;
+ coordY = 0;
+
+ memset(indexes, 0, sizeof(indexes));
+ offset = 0;
+
+ frame = NULL;
+ isLoaded = false;
+ field_D5 = 0;
+ field_D9 = 0;
+ field_DD = 0;
+ }
+
+ ~BeetleData() {
+ for (int i = 0; i < (int)sequences.size(); i++)
+ if (sequences[i])
+ delete sequences[i];
+ }
+ };
+
+ LastExpressEngine *_engine;
+
+ BeetleData *_data;
+
+ void move();
+ void updateFrame(SequenceFrame *frame) const;
+ void updateData(uint32 index);
+ void drawUpdate();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_BEETLE_H
diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp
new file mode 100644
index 0000000000..40fae3ae9b
--- /dev/null
+++ b/engines/lastexpress/game/entities.cpp
@@ -0,0 +1,2744 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/entities.h"
+
+// Data
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+
+// Entities
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/entities/abbot.h"
+#include "lastexpress/entities/alexei.h"
+#include "lastexpress/entities/alouan.h"
+#include "lastexpress/entities/anna.h"
+#include "lastexpress/entities/august.h"
+#include "lastexpress/entities/boutarel.h"
+#include "lastexpress/entities/chapters.h"
+#include "lastexpress/entities/cooks.h"
+#include "lastexpress/entities/coudert.h"
+#include "lastexpress/entities/entity39.h"
+#include "lastexpress/entities/francois.h"
+#include "lastexpress/entities/gendarmes.h"
+#include "lastexpress/entities/hadija.h"
+#include "lastexpress/entities/ivo.h"
+#include "lastexpress/entities/kahina.h"
+#include "lastexpress/entities/kronos.h"
+#include "lastexpress/entities/mahmud.h"
+#include "lastexpress/entities/max.h"
+#include "lastexpress/entities/mertens.h"
+#include "lastexpress/entities/milos.h"
+#include "lastexpress/entities/mmeboutarel.h"
+#include "lastexpress/entities/pascale.h"
+#include "lastexpress/entities/rebecca.h"
+#include "lastexpress/entities/salko.h"
+#include "lastexpress/entities/servers0.h"
+#include "lastexpress/entities/servers1.h"
+#include "lastexpress/entities/sophie.h"
+#include "lastexpress/entities/tables.h"
+#include "lastexpress/entities/tatiana.h"
+#include "lastexpress/entities/train.h"
+#include "lastexpress/entities/vassili.h"
+#include "lastexpress/entities/verges.h"
+#include "lastexpress/entities/vesna.h"
+#include "lastexpress/entities/yasmin.h"
+
+// Game
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+#define STORE_VALUE(data) ((uint)1 << (uint)data)
+
+static const EntityPosition objectsPosition[8] = {kPosition_8200, kPosition_7500,
+ kPosition_6470, kPosition_5790,
+ kPosition_4840, kPosition_4070,
+ kPosition_3050, kPosition_2740};
+
+static const EntityPosition entityPositions[41] = {
+ kPositionNone, kPosition_851, kPosition_1430, kPosition_2110, kPositionNone,
+ kPosition_2410, kPosition_2980, kPosition_3450, kPosition_3760, kPosition_4100,
+ kPosition_4680, kPosition_5140, kPosition_5440, kPosition_5810, kPosition_6410,
+ kPosition_6850, kPosition_7160, kPosition_7510, kPosition_8514, kPositionNone,
+ kPositionNone, kPositionNone, kPosition_2086, kPosition_2690, kPositionNone,
+ kPosition_3110, kPosition_3390, kPosition_3890, kPosition_4460, kPosition_4770,
+ kPosition_5090, kPosition_5610, kPosition_6160, kPosition_6460, kPosition_6800,
+ kPosition_7320, kPosition_7870, kPosition_8160, kPosition_8500, kPosition_9020,
+ kPosition_9269};
+
+#define ADD_ENTITY(class) \
+ _entities.push_back(new class(engine));
+
+#define COMPUTE_SEQUENCE_NAME(sequenceTo, sequenceFrom) { \
+ sequenceTo = sequenceFrom; \
+ for (int seqIdx = 0; seqIdx < 7; seqIdx++) \
+ sequenceTo.deleteLastChar(); \
+ if (isInsideTrainCar(entityIndex, kCarGreenSleeping) || isInsideTrainCar(entityIndex, kCarGreenSleeping)) { \
+ if (data->car < getData(kEntityPlayer)->car || (data->car == getData(kEntityPlayer)->car && data->entityPosition < getData(kEntityPlayer)->entityPosition)) \
+ sequenceTo += "R.SEQ"; \
+ else \
+ sequenceTo += "F.SEQ"; \
+ } else { \
+ sequenceTo += ".SEQ"; \
+ } \
+}
+
+#define TRY_LOAD_SEQUENCE(sequence, name, name1, name2) { \
+ if (data->car == getData(kEntityPlayer)->car) \
+ sequence = loadSequence1(name1, field30); \
+ if (sequence) { \
+ name = name1; \
+ } else { \
+ if (name2 != "") \
+ sequence = loadSequence1(name2, field30); \
+ name = (sequence ? name2 : ""); \
+ } \
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entities
+//////////////////////////////////////////////////////////////////////////
+Entities::Entities(LastExpressEngine *engine) : _engine(engine) {
+ _header = new EntityData();
+
+ _entities.push_back(NULL); // Header
+ ADD_ENTITY(Anna);
+ ADD_ENTITY(August);
+ ADD_ENTITY(Mertens);
+ ADD_ENTITY(Coudert);
+ ADD_ENTITY(Pascale);
+ ADD_ENTITY(Servers0);
+ ADD_ENTITY(Servers1);
+ ADD_ENTITY(Cooks);
+ ADD_ENTITY(Verges);
+ ADD_ENTITY(Tatiana);
+ ADD_ENTITY(Vassili);
+ ADD_ENTITY(Alexei);
+ ADD_ENTITY(Abbot);
+ ADD_ENTITY(Milos);
+ ADD_ENTITY(Vesna);
+ ADD_ENTITY(Ivo);
+ ADD_ENTITY(Salko);
+ ADD_ENTITY(Kronos);
+ ADD_ENTITY(Kahina);
+ ADD_ENTITY(Francois);
+ ADD_ENTITY(MmeBoutarel);
+ ADD_ENTITY(Boutarel);
+ ADD_ENTITY(Rebecca);
+ ADD_ENTITY(Sophie);
+ ADD_ENTITY(Mahmud);
+ ADD_ENTITY(Yasmin);
+ ADD_ENTITY(Hadija);
+ ADD_ENTITY(Alouan);
+ ADD_ENTITY(Gendarmes);
+ ADD_ENTITY(Max);
+ ADD_ENTITY(Chapters);
+ ADD_ENTITY(Train);
+
+ // Special case for tables
+ _entities.push_back(new Tables(engine, kEntityTables0));
+ _entities.push_back(new Tables(engine, kEntityTables1));
+ _entities.push_back(new Tables(engine, kEntityTables2));
+ _entities.push_back(new Tables(engine, kEntityTables3));
+ _entities.push_back(new Tables(engine, kEntityTables4));
+ _entities.push_back(new Tables(engine, kEntityTables5));
+
+ ADD_ENTITY(Entity39);
+
+ // Init compartments & positions
+ memset(&_compartments, 0, sizeof(_compartments));
+ memset(&_compartments1, 0, sizeof(_compartments1));
+ memset(&_positions, 0, sizeof(_positions));
+}
+
+Entities::~Entities() {
+ delete _header;
+
+ for (int i = 0; i < (int)_entities.size(); i++)
+ delete _entities[i];
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Accessors
+//////////////////////////////////////////////////////////////////////////
+Entity *Entities::get(EntityIndex entity) {
+ assert((uint)entity < _entities.size());
+
+ if (entity == kEntityPlayer)
+ error("Cannot get entity for index == 0!");
+
+ return _entities[entity];
+}
+
+EntityData::EntityCallData *Entities::getData(EntityIndex entity) const {
+ assert((uint)entity < _entities.size());
+
+ if (entity == kEntityPlayer)
+ return _header->getCallData();
+
+ return _entities[entity]->getData();
+}
+
+int Entities::getPosition(CarIndex car, Position position) const {
+ int index = 100 * car + position;
+
+ if (car > 10)
+ error("Entities::getPosition: trying to access an invalid car (was: %d, valid:0-9)", car);
+
+ if (position > 100)
+ error("Entities::getPosition: trying to access an invalid position (was: %d, valid:0-100)", position);
+
+ return _positions[index];
+}
+
+int Entities::getCompartments(int index) const {
+ if (index >= _compartmentsCount)
+ error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
+
+ return _compartments[index];
+}
+
+int Entities::getCompartments1(int index) const {
+ if (index >= _compartmentsCount)
+ error("Entities::getCompartments: trying to access an invalid compartment (was: %d, valid:0-15)", index);
+
+ return _compartments1[index];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame
+//////////////////////////////////////////////////////////////////////////
+void Entities::saveLoadWithSerializer(Common::Serializer &s) {
+ _header->saveLoadWithSerializer(s);
+ for (uint i = 1; i < _entities.size(); i++)
+ _entities[i]->saveLoadWithSerializer(s);
+}
+
+void Entities::savePositions(Common::Serializer &s) {
+ for (uint i = 0; i < (uint)_positionsCount; i++)
+ s.syncAsUint32LE(_positions[i]);
+}
+
+void Entities::saveCompartments(Common::Serializer &s) {
+ for (uint i = 0; i < (uint)_compartmentsCount; i++)
+ s.syncAsUint32LE(_compartments[i]);
+
+ for (uint i = 0; i < (uint)_compartmentsCount; i++)
+ s.syncAsUint32LE(_compartments1[i]);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+void Entities::setup(bool isFirstChapter, EntityIndex entityIndex) {
+ setupChapter(isFirstChapter ? kChapter1 : kChapterAll);
+
+ bool flag_4 = false;
+
+ if (!isFirstChapter) {
+ getFlags()->flag_4 = false;
+
+ if (entityIndex) {
+ getSavePoints()->call(kEntityPlayer, entityIndex, kActionNone);
+ flag_4 = getFlags()->flag_4;
+ }
+ }
+
+ getFlags()->flag_4 = flag_4;
+ if (!getFlags()->flag_4)
+ getScenes()->loadScene(getState()->scene);
+}
+
+void Entities::setupChapter(ChapterIndex chapter) {
+ if (chapter) {
+ // Reset current call, inventory item & draw sequences
+ for (uint i = 1; i < _entities.size(); i++) {
+ getData((EntityIndex)i)->currentCall = 0;
+ getData((EntityIndex)i)->inventoryItem = kItemNone;
+
+ clearSequences((EntityIndex)i);
+ }
+
+ // Init compartments & positions
+ memset(&_compartments, 0, sizeof(_compartments));
+ memset(&_compartments1, 0, sizeof(_compartments1));
+ memset(&_positions, 0, sizeof(_positions));
+
+ getSound()->resetQueue(SoundManager::kSoundType13);
+ }
+
+ // we skip the header when doing entity setup
+ for (uint i = 1; i < _entities.size(); i++) {
+ // Special case of chapters (prevents infinite loop as we will be called from Chapters functions when changing chapters)
+ if (i == kEntityChapters && chapter >= 2)
+ continue;
+
+ _entities[i]->setup(chapter);
+ }
+}
+
+void Entities::reset() {
+ // Reset header
+ delete _header;
+ _header = new EntityData();
+
+ for (uint i = 1; i < _entities.size(); i++)
+ resetSequences((EntityIndex)i);
+
+ getScenes()->resetDoorsAndClock();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// State & Sequences
+//////////////////////////////////////////////////////////////////////////
+
+EntityIndex Entities::canInteractWith(const Common::Point &point) const {
+ if (!getFlags()->isGameRunning)
+ return kEntityPlayer;
+
+ EntityIndex index = kEntityPlayer;
+ int location = 10000;
+
+ // Check if there is an entity we can interact with
+ for (uint i = 0; i < _entities.size(); i++) {
+
+ // Skip entities with no current frame
+ if (!getData((EntityIndex)i)->frame)
+ continue;
+
+ FrameInfo *info = getData((EntityIndex)i)->frame->getInfo();
+
+ // Check the hotspot
+ if (info->hotspot.contains(point)) {
+
+ // If closer to us, update with its values
+ if (location > info->location) {
+ location = info->location;
+ index = (EntityIndex)i;
+ }
+ }
+ }
+
+ // Check if we found an entity
+ if (!index)
+ return kEntityPlayer;
+
+ // Check that there is an item to interact with
+ if (!getData(index)->inventoryItem)
+ return kEntityPlayer;
+
+ return index;
+}
+
+void Entities::resetState(EntityIndex entityIndex) {
+ getData(entityIndex)->currentCall = 0;
+ getData(entityIndex)->inventoryItem = kItemNone;
+
+ if (getSound()->isBuffered(entityIndex))
+ getSound()->removeFromQueue(entityIndex);
+
+ clearSequences(entityIndex);
+
+ if (entityIndex == kEntity39)
+ entityIndex = kEntityPlayer;
+
+ if (entityIndex > kEntityChapters)
+ return;
+
+ // reset compartments and positions for this entity
+ for (int i = 0; i < _positionsCount; i++)
+ _positions[i] &= ~STORE_VALUE(entityIndex);
+
+ for (int i = 0; i < _compartmentsCount; i++) {
+ _compartments[i] &= ~STORE_VALUE(entityIndex);
+ _compartments1[i] &= ~STORE_VALUE(entityIndex);
+ }
+
+ getLogic()->updateCursor();
+}
+
+
+void Entities::updateFields() const {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ for (int i = 0; i < (int)_entities.size(); i++) {
+
+ if (!getSavePoints()->getCallback((EntityIndex)i))
+ continue;
+
+ EntityData::EntityCallData *data = getData((EntityIndex)i);
+ int positionDelta = data->field_4A3 * 10;
+ switch (data->direction) {
+ default:
+ break;
+
+ case kDirectionUp:
+ if (data->entityPosition >= 10000 - positionDelta)
+ data->entityPosition = (EntityPosition)(data->entityPosition + positionDelta);
+ break;
+
+ case kDirectionDown:
+ if (data->entityPosition > positionDelta)
+ data->entityPosition = (EntityPosition)(data->entityPosition - positionDelta);
+ break;
+
+ case kDirectionLeft:
+ data->currentFrame++;
+ break;
+
+ case kDirectionRight:
+ data->field_4A1 += 9;
+ break;
+
+ case kDirectionSwitch:
+ if (data->directionSwitch == kDirectionRight)
+ data->field_4A1 += 9;
+ break;
+
+ }
+ }
+}
+
+void Entities::updateFrame(EntityIndex entityIndex) const {
+ Sequence *sequence = NULL;
+ int16 *currentFrame = NULL;
+ bool found = false;
+
+ if (getData(entityIndex)->direction == kDirectionSwitch) {
+ sequence = getData(entityIndex)->sequence2;
+ currentFrame = &getData(entityIndex)->currentFrame2;
+ } else {
+ sequence = getData(entityIndex)->sequence;
+ currentFrame = &getData(entityIndex)->currentFrame;
+ }
+
+ if (!sequence)
+ return;
+
+ // Save current values
+ int16 oldFrame = *currentFrame;
+ int16 field_4A1 = getData(entityIndex)->field_4A1;
+
+ do {
+ // Check we do not get past the end
+ if (*currentFrame >= (int)sequence->count() - 1)
+ break;
+
+ // Get the proper frame
+ FrameInfo *info = sequence->getFrameInfo((uint16)*currentFrame);
+
+ if (info->field_33 & 8) {
+ found = true;
+ } else {
+ if (info->soundAction == 35)
+ found = true;
+
+ getData(entityIndex)->field_4A1 += info->field_30;
+
+ // Progress to the next frame
+ ++*currentFrame;
+ }
+ } while (!found);
+
+ // Restore old values
+ if (!found) {
+ *currentFrame = oldFrame;
+ getData(entityIndex)->field_4A1 = field_4A1;
+ }
+}
+
+void Entities::updateSequences() const {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ // Update the train clock & doors
+ getScenes()->updateDoorsAndClock();
+
+ //////////////////////////////////////////////////////////////////////////
+ // First pass: Drawing
+ //////////////////////////////////////////////////////////////////////////
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex entityIndex = (EntityIndex)i;
+
+ if (!getSavePoints()->getCallback(entityIndex))
+ continue;
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->frame) {
+ getScenes()->removeFromQueue(data->frame);
+ SAFE_DELETE(data->frame);
+ }
+
+ if (data->frame1) {
+ getScenes()->removeFromQueue(data->frame1);
+ SAFE_DELETE(data->frame1);
+ }
+
+ if (data->direction == kDirectionSwitch) {
+
+ // Clear sequence 2
+ if (data->sequence)
+ SAFE_DELETE(data->sequence);
+
+ // Replace by sequence 3 if available
+ if (data->sequence2) {
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+
+ data->sequence2 = NULL;
+ data->sequenceName2 = "";
+ }
+
+ data->direction = data->directionSwitch;
+ data->currentFrame = -1;
+ data->field_49B = 0;
+ }
+
+ // Draw sequences
+ drawSequences(entityIndex, data->direction, false);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Second pass: Load sequences for next pass
+ //////////////////////////////////////////////////////////////////////////
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex entityIndex = (EntityIndex)i;
+
+ if (!getSavePoints()->getCallback(entityIndex))
+ continue;
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+ byte field30 = (data->direction == kDirectionLeft ? entityIndex + 35 : 15);
+
+ if (data->sequenceName != "" && !data->sequence) {
+ data->sequence = loadSequence1(data->sequenceName, field30);
+
+ // If sequence 2 was loaded correctly, remove the copied name
+ // otherwise, compute new name
+ if (data->sequence) {
+ data->sequenceNameCopy = "";
+ } else {
+ Common::String sequenceName;
+
+ // Left and down directions
+ if (data->direction == kDirectionLeft || data->direction == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName);
+
+ // Try loading the sequence
+ data->sequence = loadSequence1(sequenceName, field30);
+ }
+
+ // Update sequence names
+ data->sequenceNameCopy = (data->sequence ? "" : data->sequenceName);
+ data->sequenceName = (data->sequence ? sequenceName : "");
+ }
+ }
+
+ // Update sequence 3
+ if (data->sequenceName2 != "" && !data->sequence2) {
+
+ if (data->car == getData(kEntityPlayer)->car)
+ data->sequence2 = loadSequence1(data->sequenceName2, field30);
+
+ if (!data->sequence2) {
+ Common::String sequenceName;
+
+ // Left and down directions
+ if (data->directionSwitch == kDirectionLeft || data->directionSwitch == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, data->sequenceName2);
+
+ // Try loading the sequence
+ data->sequence2 = loadSequence1(sequenceName, field30);
+ }
+
+ // Update sequence names
+ data->sequenceName2 = (data->sequence2 ? sequenceName : "");
+ }
+ }
+ }
+}
+
+void Entities::resetSequences(EntityIndex entityIndex) const {
+
+ // Reset direction
+ if (getData(entityIndex)->direction == kDirectionSwitch) {
+ getData(entityIndex)->direction = getData(entityIndex)->directionSwitch;
+ getData(entityIndex)->field_49B = 0;
+ getData(entityIndex)->currentFrame = -1;
+ }
+
+ // FIXME: in the original engine, the sequence pointers might just be copies,
+ // make sure we free the associated memory at some point
+ getData(entityIndex)->frame = NULL;
+ getData(entityIndex)->frame1 = NULL;
+
+ SAFE_DELETE(getData(entityIndex)->sequence);
+ SAFE_DELETE(getData(entityIndex)->sequence2);
+ SAFE_DELETE(getData(entityIndex)->sequence3);
+
+ getData(entityIndex)->field_4A9 = false;
+ getData(entityIndex)->field_4AA = false;
+
+ strcpy((char*)&getData(entityIndex)->sequenceNameCopy, "");
+ strcpy((char*)&getData(entityIndex)->sequenceName, "");
+ strcpy((char*)&getData(entityIndex)->sequenceName2, "");
+
+ // Original engine resets flag to decompress data on the fly (we don't need to do that)
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+void Entities::updateCallbacks() {
+ if (!getFlags()->isGameRunning)
+ return;
+
+ getFlags()->flag_entities_0 = false;
+
+ if (getFlags()->flag_entities_1) {
+ executeCallbacks();
+ getFlags()->flag_entities_0 = true;
+ } else {
+ getFlags()->flag_entities_1 = true;
+ executeCallbacks();
+ getFlags()->flag_entities_1 = false;
+ }
+}
+
+void Entities::executeCallbacks() {
+ for (uint i = 1; i < _entities.size(); i++) {
+ if (getFlags()->flag_entities_0)
+ break;
+
+ if (getSavePoints()->getCallback((EntityIndex)i))
+ processEntity((EntityIndex)i);
+ }
+
+ if (getFlags()->flag_entities_0)
+ return;
+
+ bool processed = true;
+ do {
+ processed = true;
+ for (int i = 1; i < (int)_entities.size(); i++) {
+ if (getFlags()->flag_entities_0)
+ break;
+
+ if (getSavePoints()->getCallback((EntityIndex)i)) {
+ if (getData((EntityIndex)i)->doProcessEntity) {
+ processed = false;
+ processEntity((EntityIndex)i);
+ }
+ }
+ }
+ } while (!processed);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Processing
+//////////////////////////////////////////////////////////////////////////
+#define INCREMENT_DIRECTION_COUNTER() { \
+ data->doProcessEntity = false; \
+ if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \
+ ++data->field_4A1; \
+ }
+
+void Entities::processEntity(EntityIndex entityIndex) {
+ EntityData::EntityCallData *data = getData(entityIndex);
+ bool keepPreviousFrame = false;
+
+ data->doProcessEntity = false;
+
+ if (getData(kEntityPlayer)->car != data->car && data->direction != kDirectionRight && data->direction != kDirectionSwitch) {
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame1) {
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ if (data->frame && data->frame->getInfo()->subType != kFrameType3) {
+ data->frame->getInfo()->subType = kFrameTypeNone;
+ getScenes()->setFlagDrawSequences();
+ }
+ }
+
+ SAFE_DELETE(data->sequence3);
+
+ if (!data->frame || !data->direction) {
+ if (!data->sequence)
+label_nosequence:
+ drawSequences(entityIndex, data->direction, true);
+
+ data->doProcessEntity = false;
+ computeCurrentFrame(entityIndex);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ if (data->sequence && data->currentFrame != -1 && data->currentFrame <= (int16)(data->sequence->count() - 1)) {
+ processFrame(entityIndex, false, true);
+
+ if (!getFlags()->flag_entities_0 && !data->doProcessEntity) {
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+ } else {
+ if (data->direction == kDirectionRight && data->field_4A1 > 100) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ INCREMENT_DIRECTION_COUNTER();
+ }
+ return;
+ }
+
+ if (!data->sequence)
+ goto label_nosequence;
+
+ if (data->frame->getInfo()->field_30 > data->field_49B + 1 || (data->direction == kDirectionLeft && data->sequence->count() == 1)) {
+ ++data->field_49B;
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) {
+ ++data->field_49B;
+ INCREMENT_DIRECTION_COUNTER();
+ return;
+ }
+
+ if (data->frame->getInfo()->keepPreviousFrame == 1)
+ keepPreviousFrame = true;
+
+ // Increment current frame
+ ++data->currentFrame;
+
+ if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) {
+
+ if (data->direction == kDirectionLeft) {
+ data->currentFrame = 0;
+ } else {
+ keepPreviousFrame = true;
+ drawNextSequence(entityIndex);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ if (!data->sequence2) {
+ updateEntityPosition(entityIndex);
+ data->doProcessEntity = false;
+ return;
+ }
+
+ copySequenceData(entityIndex);
+ }
+
+ }
+
+ processFrame(entityIndex, keepPreviousFrame, false);
+
+ if (!getFlags()->flag_entities_0 && !data->doProcessEntity)
+ INCREMENT_DIRECTION_COUNTER();
+}
+
+void Entities::computeCurrentFrame(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+ int16 originalCurrentFrame = data->currentFrame;
+
+ if (!data->sequence) {
+ data->currentFrame = -1;
+ return;
+ }
+
+ switch (data->direction) {
+ default:
+ break;
+
+ case kDirectionNone:
+ case kDirectionSwitch:
+ data->currentFrame = -1;
+ break;
+
+ case kDirectionUp:
+ case kDirectionDown: {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (scene->position > 40)
+ break;
+
+ switch (scene->position) {
+ default:
+ case 4:
+ case 19:
+ case 20:
+ case 21:
+ case 24:
+ break;
+
+ case 1:
+ case 18:
+ case 22:
+ case 40:
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (data->field_4A9) {
+ if (getData(kEntityPlayer)->entityPosition >= data->entityPosition) {
+ data->currentFrame = -1;
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
+
+ if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
+ if (data->currentFrame < (int)(data->sequence->count() - 2))
+ data->currentFrame += 2;
+ }
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ }
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (data->field_4A9) {
+ if (getData(kEntityPlayer)->entityPosition <= data->entityPosition) {
+ data->currentFrame = -1;
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, getEntityPositionFromCurrentPosition(), true);
+
+ if (data->currentFrame != -1 && originalCurrentFrame == data->currentFrame)
+ if (data->currentFrame < (int)(data->sequence->count() - 2))
+ data->currentFrame += 2;
+ }
+ } else {
+ data->currentFrame = getCurrentFrame(entityIndex, data->sequence, kPositionNone, false);
+ }
+ break;
+ }
+
+ }
+ break;
+
+
+ case kDirectionLeft:
+ if (data->currentFrame == -1 || data->currentFrame >= (int32)data->sequence->count()) {
+ data->currentFrame = 0;
+ data->field_49B = 0;
+ }
+ break;
+
+ case kDirectionRight:
+ bool found = false;
+ bool flag = false;
+ uint16 frameIndex = 0;
+ byte field30 = 0;
+
+ int16 currentFrameCopy = (!data->currentFrame && !data->field_4A1) ? -1 : data->currentFrame;
+
+ // Process frames
+ do {
+ if (frameIndex >= data->sequence->count())
+ break;
+
+ FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
+
+ if (field30 + info->field_30 >= data->field_4A1) {
+ found = true;
+ break;
+ }
+
+ if (field30 > data->field_4A1 - 10) {
+ if (info->soundAction)
+ getSound()->playSoundEvent(entityIndex, info->soundAction, (field30 <= data->field_4A1 - info->field_31) ? 0 : (byte)(field30 + info->field_31 - data->field_4A1));
+ }
+
+ field30 += info->field_30;
+
+ if (info->field_33 & 4)
+ flag = true;
+
+ if (info->field_33 & 2) {
+ flag = false;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->field_33 & 16) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ frameIndex++;
+
+ } while (!found);
+
+ if (found) {
+
+ if (flag) {
+ bool found2 = false;
+
+ do {
+ if (frameIndex >= data->sequence->count())
+ break;
+
+ FrameInfo *info = data->sequence->getFrameInfo(frameIndex);
+ if (info->field_33 & 2) {
+ found2 = true;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+
+ } else {
+ data->field_4A1 += info->field_30;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ if (soundAction)
+ getSound()->playSoundEvent(entityIndex, soundAction);
+
+ ++frameIndex;
+ }
+
+ } while (!found2);
+
+ if (found2) {
+ data->currentFrame = frameIndex;
+ data->field_49B = 0;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
+ if (soundAction && data->currentFrame != currentFrameCopy)
+ getSound()->playSoundEvent(entityIndex, soundAction, field31);
+
+ } else {
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
+ }
+
+ } else {
+
+ data->currentFrame = frameIndex;
+ data->field_49B = data->field_4A1 - field30;
+
+ byte soundAction = data->sequence->getFrameInfo((uint16)data->currentFrame)->soundAction;
+ byte field31 = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_31;
+ if (soundAction && data->currentFrame != currentFrameCopy)
+ getSound()->playSoundEvent(entityIndex, soundAction, field31 <= data->field_49B ? 0 : (byte)(field31 - data->field_49B));
+ }
+ } else {
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->field_49B = data->sequence->getFrameInfo((uint16)data->currentFrame)->field_30;
+
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+ }
+ break;
+ }
+}
+
+int16 Entities::getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const {
+ EntityData::EntityCallData *data = getData(entity);
+
+ EntityPosition firstFramePosition = sequence->getFrameInfo(0)->entityPosition;
+ EntityPosition lastFramePosition = sequence->getFrameInfo(sequence->count() - 1)->entityPosition;
+
+ bool isGoingForward = (firstFramePosition < lastFramePosition);
+
+ if (!doProcessing) {
+ if (!isGoingForward) {
+ if (data->field_4A3 + firstFramePosition < data->entityPosition || lastFramePosition - data->field_4A3 > data->entityPosition)
+ return -1;
+ } else {
+ if (firstFramePosition - data->field_4A3 > data->entityPosition || lastFramePosition + data->field_4A3 < data->entityPosition)
+ return -1;
+ }
+ }
+
+ if (sequence->count() == 0)
+ return 0;
+
+ // Search for the correct frame
+ // TODO: looks slightly like some sort of binary search
+ uint16 frame = 0;
+ uint16 numFrames = sequence->count() - 1;
+
+ for (;;) {
+ uint16 currentFrame = (frame + numFrames) / 2;
+
+ if (position + sequence->getFrameInfo(currentFrame)->entityPosition <= data->entityPosition) {
+ if (!isGoingForward)
+ numFrames = (frame + numFrames) / 2;
+ else
+ frame = (frame + numFrames) / 2;
+ } else {
+ if (isGoingForward)
+ numFrames = (frame + numFrames) / 2;
+ else
+ frame = (frame + numFrames) / 2;
+ }
+
+ if (numFrames - frame == 1) {
+ uint16 lastFramePos = ABS(position - (sequence->getFrameInfo(numFrames)->entityPosition + data->entityPosition));
+ uint16 framePosition = ABS(position - (sequence->getFrameInfo(frame)->entityPosition + data->entityPosition));
+
+ return (framePosition > lastFramePos) ? numFrames : frame;
+ }
+
+ if (numFrames <= frame)
+ return currentFrame;
+ }
+}
+
+void Entities::processFrame(EntityIndex entityIndex, bool keepPreviousFrame, bool dontPlaySound) {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ // Set frame to be drawn again
+ if (data->frame && keepPreviousFrame) {
+ if (data->frame->getInfo()->subType != kFrameType3)
+ data->frame->getInfo()->subType = kFrameType2;
+
+ getScenes()->setFlagDrawSequences();
+ }
+
+ // Remove old frame from queue
+ if (data->frame && !keepPreviousFrame)
+ getScenes()->removeFromQueue(data->frame);
+
+ // Stop if nothing else to draw
+ if (data->currentFrame < 0)
+ return;
+
+ if (data->currentFrame > (int)data->sequence->count())
+ return;
+
+ // Get new frame info
+ FrameInfo *info = data->sequence->getFrameInfo((uint16)data->currentFrame);
+
+ if (data->frame && data->frame->getInfo()->subType != kFrameType3)
+ if (!info->field_2E || keepPreviousFrame)
+ getScenes()->setCoordinates(data->frame);
+
+ // Update position
+ if (info->entityPosition) {
+ data->entityPosition = info->entityPosition;
+ if (data->field_4A9)
+ data->entityPosition = (EntityPosition)(data->entityPosition + getEntityPositionFromCurrentPosition());
+ }
+
+ info->location = entityIndex + ABS(getData(entityIndex)->entityPosition - getData(kEntityPlayer)->entityPosition);
+
+ if (info->subType != kFrameType3) {
+ info->subType = kFrameType1;
+
+ if (!keepPreviousFrame)
+ info->subType = kFrameTypeNone;
+ }
+
+ if (info->field_33 & 1)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExcuseMeCath);
+
+ if (info->field_33 & 2) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction10);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->field_33 & 16) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction4);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (data->position) {
+ updatePositionExit(entityIndex, data->car2, data->position);
+ data->car2 = kCarNone;
+ data->position = 0;
+ }
+
+ if (info->position) {
+ data->car2 = data->car;
+ data->position = info->position;
+ updatePositionEnter(entityIndex, data->car2, data->position);
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (info->soundAction && !dontPlaySound)
+ getSound()->playSoundEvent(entityIndex, info->soundAction, info->field_31);
+
+ // Add the new frame to the queue
+ SequenceFrame *frame = new SequenceFrame(data->sequence, (uint16)data->currentFrame);
+ getScenes()->addToQueue(frame);
+
+ // Keep previous frame if needed and store the new frame
+ if (keepPreviousFrame)
+ data->frame1 = data->frame;
+
+ data->frame = frame;
+
+ if (!dontPlaySound)
+ data->field_49B = keepPreviousFrame ? 0 : 1;
+}
+
+void Entities::drawNextSequence(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->direction == kDirectionRight) {
+ getSavePoints()->push(kEntityPlayer, entityIndex, kActionExitCompartment);
+ getSavePoints()->process();
+
+ if (getFlags()->flag_entities_0 || data->doProcessEntity)
+ return;
+ }
+
+ if (!isDirectionUpOrDown(entityIndex))
+ return;
+
+ if (data->sequence2)
+ return;
+
+ if (!getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingAtDoors))
+ return;
+
+ if (getData(kEntityPlayer)->car != data->car)
+ return;
+
+ if (!data->field_4A9 || isWalkingOppositeToPlayer(entityIndex)) {
+ if (!data->field_4A9 && isWalkingOppositeToPlayer(entityIndex)) {
+ data->entityPosition = kPosition_2088;
+
+ if (data->direction != kDirectionUp)
+ data->entityPosition = kPosition_8512;
+
+ drawSequences(entityIndex, data->direction, true);
+ }
+ } else {
+ data->entityPosition = kPosition_8514;
+
+ if (data->direction != kDirectionUp)
+ data->entityPosition = kPosition_2086;
+
+ drawSequences(entityIndex, data->direction, true);
+ }
+}
+
+void Entities::updateEntityPosition(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+
+ SAFE_DELETE(data->frame1);
+ data->field_49B = 0;
+
+ if (isDirectionUpOrDown(entityIndex)
+ && (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ && data->car == getData(kEntityPlayer)->car) {
+
+ if (isWalkingOppositeToPlayer(entityIndex)) {
+ data->entityPosition = getData(kEntityPlayer)->entityPosition;
+ } else if (data->field_4A9) {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_8514 : kPosition_2086;
+ } else {
+ if (isPlayerPosition(kCarGreenSleeping, 1) || isPlayerPosition(kCarGreenSleeping, 40)
+ || isPlayerPosition(kCarRedSleeping, 1) || isPlayerPosition(kCarRedSleeping, 40)) {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_2588 : kPosition_8012;
+ } else {
+ data->entityPosition = (data->direction == kDirectionUp) ? kPosition_9271 : kPosition_849;
+ }
+ }
+ }
+
+ SAFE_DELETE(data->sequence);
+ data->sequenceName = "";
+ data->field_4A9 = false;
+
+ if (data->directionSwitch)
+ data->direction = data->directionSwitch;
+}
+
+void Entities::copySequenceData(EntityIndex entityIndex) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->sequence)
+ data->sequence3 = data->sequence;
+
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+ data->field_4A9 = data->field_4AA;
+
+ if (data->directionSwitch)
+ data->direction = data->directionSwitch;
+
+ // Clear sequence 3
+ data->sequence2 = NULL;
+ data->sequenceName2 = "";
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+
+ if (data->field_4A9) {
+ computeCurrentFrame(entityIndex);
+
+ if (data->currentFrame == -1)
+ data->currentFrame = 0;
+ } else {
+ data->currentFrame = data->currentFrame2;
+ data->currentFrame2 = 0;
+
+ if (data->currentFrame == -1)
+ data->currentFrame = 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////
+void Entities::drawSequenceLeft(EntityIndex index, const char *sequence) const {
+ drawSequence(index, sequence, kDirectionLeft);
+}
+
+void Entities::drawSequenceRight(EntityIndex index, const char *sequence) const {
+ drawSequence(index, sequence, kDirectionRight);
+}
+
+void Entities::clearSequences(EntityIndex entityIndex) const {
+ debugC(8, kLastExpressDebugLogic, "Clear sequences for entity %s", ENTITY_NAME(entityIndex));
+
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ if (data->sequence2) {
+ SAFE_DELETE(data->sequence2);
+ data->sequenceName2 = "";
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+ }
+
+ if (data->sequence) {
+ SAFE_DELETE(data->sequence);
+ data->sequenceName = "";
+ data->field_4A9 = false;
+ data->currentFrame = -1;
+ }
+
+ data->sequenceNamePrefix = "";
+ data->direction = kDirectionNone;
+ data->doProcessEntity = true;
+}
+
+void Entities::drawSequence(EntityIndex index, const char *sequence, EntityDirection direction) const {
+ debugC(8, kLastExpressDebugLogic, "Drawing sequence %s for entity %s with direction %s", sequence, ENTITY_NAME(index), DIRECTION_NAME(direction));
+
+ // Copy sequence name
+ getData(index)->sequenceNamePrefix = sequence;
+ getData(index)->sequenceNamePrefix.toUppercase();
+ getData(index)->sequenceNamePrefix += "-";
+
+ // Reset fields
+ getData(index)->field_49B = 0;
+ getData(index)->currentFrame = 0;
+ getData(index)->field_4A1 = 0;
+
+ drawSequences(index, direction, true);
+}
+
+void Entities::drawSequences(EntityIndex entityIndex, EntityDirection direction, bool loadSequence) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ // Compute value for loading sequence depending on direction
+ byte field30 = (direction == kDirectionLeft ? entityIndex + 35 : 15);
+
+ data->doProcessEntity = true;
+ bool field4A9 = data->field_4A9;
+
+ // First case: different car and not going right: cleanup and return
+ if (data->car != getData(kEntityPlayer)->car && direction != kDirectionRight) {
+ clearEntitySequenceData(data, direction);
+ return;
+ }
+
+ data->directionSwitch = kDirectionNone;
+
+ // Process sequence names
+ Common::String sequenceName;
+ Common::String sequenceName1;
+ Common::String sequenceName2;
+ Common::String sequenceName3;
+
+ getSequenceName(entityIndex, direction, sequenceName1, sequenceName2);
+
+ // No sequence 1: cleanup and return
+ if (sequenceName1 == "") {
+ clearEntitySequenceData(data, direction);
+ return;
+ }
+
+ if (sequenceName1 == data->sequenceNameCopy) {
+ data->direction = direction;
+ return;
+ }
+
+ if (direction == kDirectionLeft || direction == kDirectionRight) {
+ COMPUTE_SEQUENCE_NAME(sequenceName, sequenceName1);
+
+ if (sequenceName3 != "")
+ COMPUTE_SEQUENCE_NAME(sequenceName3, sequenceName2);
+ }
+
+ if (!data->frame) {
+ data->direction = direction;
+
+ if (sequenceName1 == data->sequenceName) {
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ SAFE_DELETE(data->sequence);
+
+ if (sequenceName1 != data->sequenceName2) {
+
+ if (loadSequence) {
+
+ if (data->car == getData(kEntityPlayer)->car)
+ data->sequence = loadSequence1(sequenceName1, field30);
+
+ if (data->sequence) {
+ data->sequenceName = sequenceName1;
+ data->sequenceNameCopy = "";
+ } else {
+ if (sequenceName != "")
+ data->sequence = loadSequence1(sequenceName, field30);
+
+ data->sequenceName = (data->sequence ? sequenceName : "");
+ data->sequenceNameCopy = (data->sequence ? "" : sequenceName1);
+ }
+ } else {
+ data->sequenceName = sequenceName1;
+ }
+
+ if (sequenceName2 != "") {
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ if (!data->sequence2) {
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ SAFE_DELETE(data->sequence2);
+ } else {
+ data->sequence = data->sequence2;
+ data->sequenceName = data->sequenceName2;
+ data->sequence2 = NULL;
+ }
+
+ data->sequenceName2 = "";
+
+ if (sequenceName2 == "")
+ return;
+
+ loadSequence2(entityIndex, sequenceName2, sequenceName3, field30, loadSequence);
+ return;
+ }
+
+ if (data->sequenceName != sequenceName1) {
+
+ if (data->sequenceName2 != sequenceName1) {
+ SAFE_DELETE(data->sequence2);
+ TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName1, sequenceName);
+ }
+
+ data->field_4AA = data->field_4A9;
+ if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
+ data->currentFrame2 = 0;
+ } else {
+ data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
+
+ if (data->currentFrame2 == -1) {
+ clearSequences(entityIndex);
+ return;
+ }
+ }
+
+ data->field_4A9 = field4A9;
+ data->field_49B = data->frame->getInfo()->field_30;
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->direction = kDirectionSwitch;
+ data->directionSwitch = direction;
+ } else {
+ SAFE_DELETE(data->sequence2);
+
+ data->sequence2 = loadSequence1(data->sequence->getName(), data->sequence->getField30());
+
+ data->sequenceName2 = data->sequenceName;
+ data->field_4AA = data->field_4A9;
+ data->field_49B = data->frame->getInfo()->field_30;
+ data->currentFrame = (int16)(data->sequence->count() - 1);
+ data->direction = kDirectionSwitch;
+ data->directionSwitch = direction;
+
+ if ((direction != kDirectionUp && direction != kDirectionDown) || data->field_4AA || !data->sequence2) {
+ data->currentFrame2 = 0;
+ } else {
+ data->currentFrame2 = getCurrentFrame(entityIndex, data->sequence2, kPositionNone, false);
+
+ if (data->currentFrame2 == -1)
+ clearSequences(entityIndex);
+ }
+ }
+}
+
+void Entities::loadSequence2(EntityIndex entityIndex, Common::String sequenceName, Common::String sequenceName2, byte field30, bool reloadSequence) const {
+ EntityData::EntityCallData *data = getData(entityIndex);
+
+ if (data->sequenceName2 == sequenceName)
+ return;
+
+ if (data->sequence2)
+ SAFE_DELETE(data->sequence2);
+
+ if (reloadSequence) {
+ TRY_LOAD_SEQUENCE(data->sequence2, data->sequenceName2, sequenceName, sequenceName2);
+ } else {
+ data->sequenceName2 = sequenceName;
+ }
+}
+
+void Entities::getSequenceName(EntityIndex index, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const {
+ EntityData::EntityCallData *data = getData(index);
+ Position position = getScenes()->get(getState()->scene)->position;
+
+ // reset fields
+ data->field_4A9 = false;
+ data->field_4AA = false;
+
+ switch (direction) {
+ default:
+ break;
+
+ case kDirectionUp:
+ switch (position) {
+ default:
+ break;
+
+ case 1:
+ if (data->entityPosition < kPosition_2587)
+ sequence1 = Common::String::printf("%02d%01d-01u.seq", index, data->clothes);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (data->entityPosition >= kPosition_9270)
+ break;
+
+ if (data->entityPosition >= kPosition_8513) {
+ sequence1 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
+ } else {
+ sequence1 = Common::String::printf("%02d%01d-03u.seq", index, data->clothes);
+ sequence2 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
+ data->field_4A9 = true;
+ }
+ break;
+
+ case 18:
+ if (data->entityPosition < kPosition_9270)
+ sequence1 = Common::String::printf("%02d%01d-18u.seq", index, data->clothes);
+ break;
+
+ case 22:
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
+ sequence1 = Common::String::printf("%02d%01d-22u.seq", index, data->clothes);
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (getData(kEntityPlayer)->entityPosition <= data->entityPosition)
+ break;
+
+ if (data->entityPosition >= kPosition_2087) {
+ sequence1 = Common::String::printf("%02d%01d-38u.seq", index, data->clothes);
+ data->field_4A9 = true;
+ } else {
+ sequence1 = Common::String::printf("%02d%01d-%02deu.seq", index, data->clothes, position);
+ sequence2 = Common::String::printf("%02d%01d-38u.seq", index, data->clothes);
+ data->field_4AA = true;
+ }
+ break;
+
+ case 40:
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition)
+ sequence1 = Common::String::printf("%02d%01d-40u.seq", index, data->clothes);
+ break;
+ }
+ break;
+
+ case kDirectionDown:
+ switch (position) {
+ default:
+ break;
+
+ case 1:
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
+ sequence1 = Common::String::printf("%02d%01d-01d.seq", index, data->clothes);
+ break;
+
+ case 2:
+ case 3:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ if (getData(kEntityPlayer)->entityPosition >= data->entityPosition)
+ break;
+
+ if (data->entityPosition <= kPosition_8513) {
+ sequence1 = Common::String::printf("%02d%01d-03d.seq", index, data->clothes);
+ data->field_4A9 = true;
+ } else {
+ sequence1 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
+ sequence2 = Common::String::printf("%02d%01d-03d.seq", index, data->clothes);
+ data->field_4AA = true;
+ }
+ break;
+
+ case 18:
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition)
+ sequence1 = Common::String::printf("%02d%01d-18d.seq", index, data->clothes);
+ break;
+
+ case 22:
+ if (data->entityPosition > kPosition_850)
+ sequence1 = Common::String::printf("%02d%01d-22d.seq", index, data->clothes);
+ break;
+
+ case 23:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ if (data->entityPosition <= kPosition_850)
+ break;
+
+ if (data->entityPosition <= kPosition_2087) {
+ sequence1 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
+ } else {
+ sequence1 = Common::String::printf("%02d%01d-38d.seq", index, data->clothes);
+ sequence2 = Common::String::printf("%02d%01d-%02ded.seq", index, data->clothes, position);
+ data->field_4A9 = true;
+ }
+ break;
+
+ case 40:
+ if (getData(kEntityPlayer)->entityPosition > kPosition_8013)
+ sequence1 = Common::String::printf("%02d%01d-40d.seq", index, data->clothes);
+ break;
+ }
+ break;
+
+ // First part of sequence is already set
+ case kDirectionLeft:
+ case kDirectionRight:
+ sequence1 = Common::String::printf("%s%02d.seq", data->sequenceNamePrefix.c_str(), position);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// Compartments
+//////////////////////////////////////////////////////////////////////////
+void Entities::enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
+ if (entity > kEntityChapters)
+ return;
+
+ switch (compartment) {
+ default:
+ // Return here so we do not update the compartments
+ return;
+
+ case kObjectCompartment1:
+ updatePositionsEnter(entity, kCarGreenSleeping, 41, 51, 17, 38);
+ break;
+
+ case kObjectCompartment2:
+ updatePositionsEnter(entity, kCarGreenSleeping, 42, 52, 15, 36);
+ break;
+
+ case kObjectCompartment3:
+ updatePositionsEnter(entity, kCarGreenSleeping, 43, 53, 13, 34);
+ break;
+
+ case kObjectCompartment4:
+ updatePositionsEnter(entity, kCarGreenSleeping, 44, 54, 11, 32);
+ break;
+
+ case kObjectCompartment5:
+ updatePositionsEnter(entity, kCarGreenSleeping, 45, 55, 9, 30);
+ break;
+
+ case kObjectCompartment6:
+ updatePositionsEnter(entity, kCarGreenSleeping, 46, 56, 7, 28);
+ break;
+
+ case kObjectCompartment7:
+ updatePositionsEnter(entity, kCarGreenSleeping, 47, 57, 5, 26);
+ break;
+
+ case kObjectCompartment8:
+ updatePositionsEnter(entity, kCarGreenSleeping, 48, 58, 3, 25);
+ break;
+
+ case kObjectCompartmentA:
+ updatePositionsEnter(entity, kCarRedSleeping, 41, 51, 17, 38);
+ break;
+
+ case kObjectCompartmentB:
+ updatePositionsEnter(entity, kCarRedSleeping, 42, 52, 15, 36);
+ break;
+
+ case kObjectCompartmentC:
+ updatePositionsEnter(entity, kCarRedSleeping, 43, 53, 13, 34);
+ break;
+
+ case kObjectCompartmentD:
+ updatePositionsEnter(entity, kCarRedSleeping, 44, 54, 11, 32);
+ break;
+
+ case kObjectCompartmentE:
+ updatePositionsEnter(entity, kCarRedSleeping, 45, 55, 9, 30);
+ break;
+
+ case kObjectCompartmentF:
+ updatePositionsEnter(entity, kCarRedSleeping, 46, 56, 7, 28);
+ break;
+
+ case kObjectCompartmentG:
+ updatePositionsEnter(entity, kCarRedSleeping, 47, 57, 5, 26);
+ break;
+
+ case kObjectCompartmentH:
+ updatePositionsEnter(entity, kCarRedSleeping, 48, 58, 3, 25);
+ break;
+ }
+
+ // Update compartments
+ int index = (compartment < 32 ? compartment - 1 : compartment - 24);
+ if (index >= 16)
+ error("Entities::exitCompartment: invalid compartment index!");
+
+ if (useCompartment1)
+ _compartments1[index] |= STORE_VALUE(entity);
+ else
+ _compartments[index] |= STORE_VALUE(entity);
+}
+
+void Entities::exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1) {
+ if (entity > kEntityChapters)
+ return;
+
+ // TODO factorize in one line
+ switch (compartment) {
+ default:
+ // Return here so we do not update the compartments
+ return;
+
+ case kObjectCompartment1:
+ updatePositionsExit(entity, kCarGreenSleeping, 41, 51);
+ break;
+
+ case kObjectCompartment2:
+ updatePositionsExit(entity, kCarGreenSleeping, 42, 52);
+ break;
+
+ case kObjectCompartment3:
+ updatePositionsExit(entity, kCarGreenSleeping, 43, 53);
+ break;
+
+ case kObjectCompartment4:
+ updatePositionsExit(entity, kCarGreenSleeping, 44, 54);
+ break;
+
+ case kObjectCompartment5:
+ updatePositionsExit(entity, kCarGreenSleeping, 45, 55);
+ break;
+
+ case kObjectCompartment6:
+ updatePositionsExit(entity, kCarGreenSleeping, 46, 56);
+ break;
+
+ case kObjectCompartment7:
+ updatePositionsExit(entity, kCarGreenSleeping, 47, 57);
+ break;
+
+ case kObjectCompartment8:
+ updatePositionsExit(entity, kCarGreenSleeping, 48, 58);
+ break;
+
+ case kObjectCompartmentA:
+ updatePositionsExit(entity, kCarRedSleeping, 41, 51);
+ break;
+
+ case kObjectCompartmentB:
+ updatePositionsExit(entity, kCarRedSleeping, 42, 52);
+ break;
+
+ case kObjectCompartmentC:
+ updatePositionsExit(entity, kCarRedSleeping, 43, 53);
+ break;
+
+ case kObjectCompartmentD:
+ updatePositionsExit(entity, kCarRedSleeping, 44, 54);
+ break;
+
+ case kObjectCompartmentE:
+ updatePositionsExit(entity, kCarRedSleeping, 45, 55);
+ break;
+
+ case kObjectCompartmentF:
+ updatePositionsExit(entity, kCarRedSleeping, 46, 56);
+ break;
+
+ case kObjectCompartmentG:
+ updatePositionsExit(entity, kCarRedSleeping, 47, 57);
+ break;
+
+ case kObjectCompartmentH:
+ updatePositionsExit(entity, kCarRedSleeping, 48, 58);
+ break;
+ }
+
+ // Update compartments
+ int index = (compartment < 32 ? compartment - 1 : compartment - 24);
+ if (index >= 16)
+ error("Entities::exitCompartment: invalid compartment index!");
+
+ if (useCompartment1)
+ _compartments1[index] &= ~STORE_VALUE(entity);
+ else
+ _compartments[index] &= ~STORE_VALUE(entity);
+}
+
+void Entities::updatePositionEnter(EntityIndex entity, CarIndex car, Position position) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position] |= STORE_VALUE(entity);
+
+ if (isPlayerPosition(car, position) || (car == kCarRestaurant && position == 57 && isPlayerPosition(kCarRestaurant, 50))) {
+ getSound()->excuseMe(entity);
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ } else {
+ getLogic()->updateCursor();
+ }
+}
+
+void Entities::updatePositionExit(EntityIndex entity, CarIndex car, Position position) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position] &= ~STORE_VALUE(entity);
+
+ getLogic()->updateCursor();
+}
+
+void Entities::updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position1] |= STORE_VALUE(entity);
+ _positions[100 * car + position2] |= STORE_VALUE(entity);
+
+ // FIXME: also checking two DWORD values that do not seem to updated anywhere...
+ if (isPlayerPosition(car, position1) || isPlayerPosition(car, position2) || isPlayerPosition(car, position3) || isPlayerPosition(car, position4)) {
+ getSound()->excuseMe(entity);
+ getScenes()->loadScene(getScenes()->processIndex(getState()->scene));
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ } else {
+ getLogic()->updateCursor();
+ }
+}
+
+void Entities::updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2) {
+ if (entity == kEntity39)
+ entity = kEntityPlayer;
+
+ if (entity > kEntityChapters)
+ return;
+
+ _positions[100 * car + position1] &= ~STORE_VALUE(entity);
+ _positions[100 * car + position2] &= ~STORE_VALUE(entity);
+
+ getLogic()->updateCursor();
+}
+
+void Entities::loadSceneFromEntityPosition(CarIndex car, EntityPosition entityPosition, bool alternate) const {
+
+ // Determine position
+ Position position = (alternate ? 1 : 40);
+ do {
+ if (entityPosition > entityPositions[position]) {
+ if (alternate)
+ break;
+
+ // For default value, we ignore position 24
+ if (position != 24)
+ break;
+ }
+
+ alternate ? ++position : --position;
+
+ } while (alternate ? position <= 18 : position >= 22);
+
+ // For position outside bounds, use minimal value
+ if ((alternate && position > 18) || (alternate && position < 22)) {
+ getScenes()->loadSceneFromPosition(car, alternate ? 18 : 22);
+ return;
+ }
+
+ // Load scene from position
+ switch (position) {
+ default:
+ getScenes()->loadSceneFromPosition(car, (Position)(position + (alternate ? - 1 : 1)));
+ break;
+
+ // Alternate
+ case 1:
+ if (alternate) getScenes()->loadSceneFromPosition(car, 1);
+ break;
+
+ case 5:
+ if (alternate) getScenes()->loadSceneFromPosition(car, 3);
+ break;
+
+ // Default
+ case 23:
+ if (!alternate) getScenes()->loadSceneFromPosition(car, 25);
+ break;
+
+ case 40:
+ if (!alternate) getScenes()->loadSceneFromPosition(car, 40);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+bool Entities::hasValidFrame(EntityIndex entity) const {
+ return (getData(entity)->frame && (getData(entity)->frame->getInfo()->subType != kFrameType3));
+}
+
+bool Entities::compare(EntityIndex entity1, EntityIndex entity2) const {
+ EntityData::EntityCallData *data1 = getData(entity1);
+ EntityData::EntityCallData *data2 = getData(entity2);
+
+ if (data2->car != data1->car
+ || data1->car < kCarGreenSleeping
+ || data1->car > kCarRedSleeping)
+ return false;
+
+ EntityPosition position1 = (data1->entityPosition >= data2->entityPosition) ? data1->entityPosition : data2->entityPosition;
+ EntityPosition position2 = (data1->entityPosition >= data2->entityPosition) ? data2->entityPosition : data1->entityPosition;
+
+ // Compute position
+ int index1 = 7;
+ do {
+ if (objectsPosition[index1] >= position2)
+ break;
+
+ --index1;
+ } while (index1 > -1);
+
+ int index2 = 0;
+ do {
+ if (objectsPosition[index2] <= position2)
+ break;
+
+ ++index2;
+ } while (index2 < 8);
+
+ if (index1 > -1 && index2 < 8 && index2 <= index1) {
+ while (index2 <= index1) {
+ if (getCompartments(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
+ return true;
+
+ if (getCompartments1(index2 + (data1->car == kCarGreenSleeping ? 0 : 8)))
+ return true;
+
+ ++index2;
+ }
+ }
+
+ for (EntityIndex entity = kEntityAnna; entity <= kEntity39; entity = (EntityIndex)(entity + 1)) {
+
+ if (entity1 == entity || entity2 == entity)
+ continue;
+
+ if (!isDirectionUpOrDown(entity))
+ continue;
+
+ if (data1->car == getEntityData(entity)->car
+ && getEntityData(entity)->entityPosition > position2
+ && getEntityData(entity)->entityPosition < position1)
+ return true;
+ }
+
+ return false;
+}
+
+bool Entities::updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const {
+ EntityData::EntityCallData *data = getData(entity);
+ EntityDirection direction = kDirectionNone;
+ int delta = 0;
+ bool flag1 = false;
+ bool flag2 = false;
+ bool flag3 = false;
+
+ if (position == kPosition_2000
+ && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
+ && !isPlayerPosition(kCarGreenSleeping, 1)
+ && !isPlayerPosition(kCarRedSleeping, 2))
+ position = kPosition_1500;
+
+ if (data->direction != kDirectionUp && data->direction != kDirectionDown)
+ data->field_497 = 0;
+
+ if (data->field_497) {
+ data->field_497--;
+
+ if (data->field_497 == 128)
+ data->field_497 = 0;
+
+ if ((data->field_497 & 127) != 8) {
+ data->field_49B = 0;
+ return false;
+ }
+
+ flag1 = true;
+
+ if (data->field_497 & 128)
+ flag2 = true;
+ }
+
+ if (data->car != car)
+ goto label_process_entity;
+
+ // Calculate delta
+ delta = ABS(data->entityPosition - position);
+ if (delta < 100 || (position > kPosition_850 && position < kPosition_9270 && delta < 300))
+ flag3 = true;
+
+ if (!flag3) {
+ if ((getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && data->direction == kDirectionUp)
+ || (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && data->direction == kDirectionDown)) {
+ if (!checkPosition(position) && isDistanceBetweenEntities(entity, kEntityPlayer, 250))
+ flag3 = true;
+ }
+
+ if (!flag3)
+ goto label_process_entity;
+ }
+
+ if (getEntities()->hasValidFrame(entity)
+ && getEntities()->isWalkingOppositeToPlayer(entity)
+ && !getEntities()->checkPosition(position)) {
+ flag3 = false;
+ position = (EntityPosition)(getData(kEntityPlayer)->entityPosition + 250 * (data->direction == kDirectionUp ? 1 : -1));
+ }
+
+ if (!flag3) {
+label_process_entity:
+
+ // Calculate direction
+ if (data->car < car)
+ direction = kDirectionUp;
+ else if (data->car > car)
+ direction = kDirectionDown;
+ else // same car
+ direction = (data->entityPosition < position) ? kDirectionUp : kDirectionDown;
+
+ if (data->direction == direction) {
+ if (!flag1) {
+
+ if (checkDistanceFromPosition(entity, kPosition_1500, 750) && entity != kEntityFrancois) {
+
+ if (data->entity != kEntityPlayer) {
+ if (data->direction != kDirectionUp || (position <= kPosition_2000 && data->car == car)) {
+ if (data->direction == kDirectionDown && (position < kPosition_1500 || data->car != car)) {
+ if (data->entityPosition > kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
+ data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
+ getSavePoints()->push(entity, data->entity, kAction11);
+ }
+ }
+ } else {
+ if (data->entityPosition < kPosition_1500 && (data->car == kCarGreenSleeping || data->car == kCarRedSleeping)) {
+ data->entity = (data->car == kCarGreenSleeping) ? kEntityMertens : kEntityCoudert;
+ getSavePoints()->push(entity, data->entity, kAction11, 1);
+ }
+ }
+ }
+
+ } else if (data->entity) {
+ getSavePoints()->push(entity, data->entity, kAction16);
+ data->entity = kEntityPlayer;
+ }
+
+ if (hasValidFrame(entity)) {
+
+ if (!data->field_4A9)
+ return false;
+
+ int compartmentIndex = 0;
+ if (data->car == kCarGreenSleeping)
+ compartmentIndex = 0;
+ else if (data->car == kCarRedSleeping)
+ compartmentIndex = 8;
+
+ for (int i = 0; i < 8; i++) {
+ if (getCompartments(compartmentIndex) || getCompartments1(compartmentIndex)) {
+ if (checkDistanceFromPosition(entity, objectsPosition[i], 750)) {
+ if (checkPosition(objectsPosition[i])) {
+
+ if ((data->direction == kDirectionUp && data->entityPosition < objectsPosition[i] && (data->car != car || position > objectsPosition[i]))
+ || (data->direction == kDirectionDown && data->entityPosition > objectsPosition[i] && (data->car != car || position < objectsPosition[i]))) {
+
+ getSound()->excuseMe(entity, (EntityIndex)(State::getPowerOfTwo((uint32)(getCompartments(compartmentIndex) ? getCompartments(compartmentIndex) : getCompartments1(compartmentIndex)))));
+
+ data->field_497 = 144;
+
+ break;
+ }
+ }
+ }
+ }
+
+ compartmentIndex++;
+ }
+
+ for (EntityIndex entityIndex = kEntityAnna; entityIndex <= kEntity39; entityIndex = (EntityIndex)(entityIndex + 1)) {
+ if (getSavePoints()->getCallback(entityIndex)
+ && hasValidFrame(entityIndex)
+ && entityIndex != entity
+ && isDistanceBetweenEntities(entity, entityIndex, 750)
+ && isDirectionUpOrDown(entityIndex)
+ && (entity != kEntityRebecca || entityIndex != kEntitySophie)
+ && (entity != kEntitySophie || entityIndex != kEntityRebecca)
+ && (entity != kEntityIvo || entityIndex != kEntitySalko)
+ && (entity != kEntitySalko || entityIndex != kEntityIvo)
+ && (entity != kEntityMilos || entityIndex != kEntityVesna)
+ && (entity != kEntityVesna || entityIndex != kEntityMilos)) {
+
+ EntityData::EntityCallData *data2 = getData(entityIndex);
+
+ if (data->direction != data2->direction) {
+
+ if ((data->direction != kDirectionUp || data2->entityPosition <= data->entityPosition)
+ && (data->direction != kDirectionDown || data2->entityPosition >= data->entityPosition))
+ continue;
+
+ data->field_49B = 0;
+ data2->field_49B = 0;
+
+ data->field_497 = 16;
+ data2->field_497 = 16;
+
+ getSound()->excuseMe(entity, entityIndex);
+ getSound()->excuseMe(entityIndex, entity);
+
+ if (entityIndex > entity)
+ ++data2->field_497;
+
+ break;
+ }
+
+ if (ABS(data2->entityPosition - getData(kEntityPlayer)->entityPosition) < ABS(data->entityPosition - getData(kEntityPlayer)->entityPosition)) {
+
+ if (!isWalkingOppositeToPlayer(entity)) {
+
+ if (direction == kDirectionUp) {
+ if (data->entityPosition < kPosition_9500)
+ data->entityPosition = (EntityPosition)(data->entityPosition + 500);
+ } else {
+ if (data->entityPosition > kPosition_500)
+ data->entityPosition = (EntityPosition)(data->entityPosition - 500);
+ }
+
+ drawSequences(entity, direction, true);
+
+ return false;
+ }
+ data->field_49B = 0;
+
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ if (data->direction == kDirectionUp) {
+ if (data->entityPosition + data->field_4A3 < 10000)
+ data->entityPosition = (EntityPosition)(data->entityPosition + data->field_4A3);
+ } else {
+ if (data->entityPosition > data->field_4A3)
+ data->entityPosition = (EntityPosition)(data->entityPosition - data->field_4A3);
+ }
+
+ if (data->entityPosition <= kPosition_9270 || data->direction != kDirectionUp) {
+ if (data->entityPosition < kPosition_850 && data->direction == kDirectionDown) {
+ if (changeCar(data, entity, car, position, false, kPosition_9269, kCarKronos))
+ return true;
+ }
+ } else {
+ if (changeCar(data, entity, car, position, true, kPosition_851, kCarGreenSleeping))
+ return true;
+ }
+
+ if (getData(kEntityPlayer)->car == data->car && data->location == kLocationOutsideCompartment) {
+ if (data->direction == kDirectionUp) {
+
+ if (getData(kEntityPlayer)->entityPosition > data->entityPosition
+ && getData(kEntityPlayer)->entityPosition - data->entityPosition >= 500
+ && data->field_4A3 + 500 > getData(kEntityPlayer)->entityPosition - data->entityPosition) {
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) || getScenes()->checkCurrentPosition(false)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
+
+ if (getScenes()->checkCurrentPosition(false))
+ getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1, true);
+
+ } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
+ }
+ }
+ } else {
+ if (getData(kEntityPlayer)->entityPosition < data->entityPosition
+ && data->entityPosition - getData(kEntityPlayer)->entityPosition >= 500
+ && data->field_4A3 + 500 > data->entityPosition - getData(kEntityPlayer)->entityPosition) {
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) {
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath);
+ } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){
+ getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe);
+
+ if (getScenes()->checkCurrentPosition(false))
+ getScenes()->loadSceneFromObject((ObjectIndex)getScenes()->get(getState()->scene)->param1);
+ }
+ }
+ }
+ return false;
+ }
+ }
+ } else if (!flag1) {
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust positions
+
+ // Direction Up
+ if (direction == kDirectionUp) {
+ if (data->entityPosition < (flag2 ? kPosition_8800 : kPosition_9250))
+ data->entityPosition = (EntityPosition)(data->entityPosition + (flag2 ? kPosition_1200 : kPosition_750));
+
+ if (data->car == car && data->entityPosition >= position) {
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+ return true;
+ }
+
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ // Direction Down
+ if (data->entityPosition > (flag2 ? kPosition_1200 : kPosition_750))
+ data->entityPosition = (EntityPosition)(data->entityPosition - (flag2 ? kPosition_1200 : kPosition_750));
+
+ if (data->car == car && data->entityPosition <= position) {
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+ return true;
+ }
+
+ drawSequences(entity, direction, true);
+ return false;
+ }
+
+ data->entityPosition = position;
+ if (data->direction == kDirectionUp || data->direction == kDirectionDown)
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+
+ return true;
+}
+
+bool Entities::changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const {
+ if (getData(kEntityPlayer)->car == data->car) {
+ getSound()->playSoundEvent(entity, 36);
+ getSound()->playSoundEvent(entity, 37, 30);
+ }
+
+ data->car = (CarIndex)(increment ? data->car + 1 : data->car - 1);
+ data->entityPosition = newPosition;
+
+ if (data->car == newCar) {
+ if (isInGreenCarEntrance(kEntityPlayer)) {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 1);
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ getSound()->playSoundEvent(kEntityPlayer, 15);
+ }
+ }
+
+ if ((increment ? data->car > car : data->car < car) || (data->car == car && (increment ? data->entityPosition >= position : data->entityPosition <= position))) {
+ data->car = car;
+ data->entityPosition = position;
+ data->direction = kDirectionNone;
+ data->entity = kEntityPlayer;
+
+ return true;
+ }
+
+ if (data->car == newCar) {
+ if (isInKronosCarEntrance(kEntityPlayer)) {
+ getSound()->playSoundEvent(kEntityPlayer, 14);
+ getSound()->excuseMe(entity, kEntityPlayer, SoundManager::kFlagDefault);
+ getScenes()->loadSceneFromPosition(kCarGreenSleeping, 62);
+ getSound()->playSound(kEntityPlayer, "CAT1127A");
+ getSound()->playSoundEvent(kEntityPlayer, 15);
+ }
+ }
+
+ if (data->car == getData(kEntityPlayer)->car) {
+ getSound()->playSoundEvent(entity, 36);
+ getSound()->playSoundEvent(entity, 37, 30);
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// CHECKS
+//////////////////////////////////////////////////////////////////////////
+bool Entities::isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const {
+ return (getData(entity)->entityPosition == position
+ && getData(entity)->location == kLocationInsideCompartment
+ && getData(entity)->car == car);
+}
+
+bool Entities::checkFields2(ObjectIndex object) const {
+
+ EntityPosition position = kPositionNone;
+ CarIndex car = kCarNone;
+
+ switch (object) {
+ default:
+ return false;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ position = objectsPosition[object - 1];
+ car = kCarGreenSleeping;
+ if (isInsideCompartment(kEntityPlayer, car, position))
+ return false;
+ break;
+
+ case kObjectHandleBathroom:
+ case kObjectHandleInsideBathroom:
+ case kObjectKitchen:
+ case kObject20:
+ case kObject21:
+ case kObject22:
+ position = objectsPosition[object-17];
+ car = kCarGreenSleeping;
+ break;
+
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ case kObjectCompartmentG:
+ case kObjectCompartmentH:
+ position = objectsPosition[object-32];
+ car = kCarRedSleeping;
+ if (isInsideCompartment(kEntityPlayer, car, position))
+ return false;
+ break;
+
+ case kObject48:
+ case kObject49:
+ case kObject50:
+ case kObject51:
+ case kObject52:
+ case kObject53:
+ position = objectsPosition[object-48];
+ car = kCarRedSleeping;
+ break;
+
+ }
+
+ uint index = 1;
+ while (!isInsideCompartment((EntityIndex)index, car, position) || index == kEntityVassili) {
+ index++;
+ if (index >= 40)
+ return false;
+ }
+
+ return true;
+}
+
+bool Entities::isInsideCompartments(EntityIndex entity) const {
+ return (getData(entity)->car == kCarGreenSleeping
+ || getData(entity)->car == kCarRedSleeping)
+ && getData(entity)->location == kLocationInsideCompartment;
+}
+
+bool Entities::isPlayerPosition(CarIndex car, Position position) const {
+ return getData(kEntityPlayer)->car == car && getScenes()->get(getState()->scene)->position == position;
+}
+
+bool Entities::isInsideTrainCar(EntityIndex entity, CarIndex car) const {
+ return getData(entity)->car == car && getData(entity)->location <= kLocationInsideCompartment;
+}
+
+bool Entities::isInGreenCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarGreenSleeping) && getData(entity)->entityPosition < kPosition_850;
+}
+
+bool Entities::isPlayerInCar(CarIndex car) const {
+ return isInsideTrainCar(kEntityPlayer, car) && getData(kEntityPlayer)->location && !isInGreenCarEntrance(kEntityPlayer);
+}
+
+bool Entities::isDirectionUpOrDown(EntityIndex entity) const {
+ return getData(entity)->direction == kDirectionUp || getData(entity)->direction == kDirectionDown;
+}
+
+bool Entities::isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const {
+ return getData(entity1)->car == getData(entity2)->car
+ && (uint)ABS(getData(entity1)->entityPosition - getData(entity2)->entityPosition) <= distance
+ && (getData(entity1)->location != kLocationOutsideTrain || getData(entity2)->location != kLocationOutsideTrain);
+}
+
+bool Entities::checkFields10(EntityIndex entity) const {
+ return getData(entity)->location <= kLocationOutsideTrain;
+}
+
+bool Entities::isSomebodyInsideRestaurantOrSalon() const {
+ for (uint i = 1; i < _entities.size(); i++) {
+ EntityIndex index = (EntityIndex)i;
+
+ if (getData(index)->location == kLocationOutsideCompartment && (isInSalon(index) || isInRestaurant(index)))
+ return false;
+ }
+
+ return true;
+}
+
+bool Entities::isInSalon(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant)
+ && getData(entity)->entityPosition >= kPosition_1540
+ && getData(entity)->entityPosition <= kPosition_3650;
+}
+
+bool Entities::isInRestaurant(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant)
+ && getData(entity)->entityPosition >= kPosition_3650
+ && getData(entity)->entityPosition <= kPosition_5800;
+}
+
+bool Entities::isInKronosSalon(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos)
+ && getData(entity)->entityPosition >= kPosition_5500
+ && getData(entity)->entityPosition <= kPosition_7500;
+}
+
+bool Entities::isOutsideAlexeiWindow() const {
+ return (getData(kEntityPlayer)->entityPosition == kPosition_7500 || getData(kEntityPlayer)->entityPosition == kPosition_8200)
+ && getData(kEntityPlayer)->location == kLocationOutsideTrain
+ && getData(kEntityPlayer)->car == kCarGreenSleeping;
+}
+
+bool Entities::isOutsideAnnaWindow() const {
+ return (getData(kEntityPlayer)->entityPosition == kPosition_4070 || getData(kEntityPlayer)->entityPosition == kPosition_4840)
+ && getData(kEntityPlayer)->location == kLocationOutsideTrain
+ && getData(kEntityPlayer)->car == kCarRedSleeping;
+}
+
+bool Entities::isInKitchen(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarRestaurant) && getData(entity)->entityPosition > kPosition_5800;
+}
+
+bool Entities::isNobodyInCompartment(CarIndex car, EntityPosition position) const {
+ for (uint i = 1; i < _entities.size(); i++) {
+ if (isInsideCompartment((EntityIndex)i, car, position))
+ return false;
+ }
+ return true;
+}
+
+bool Entities::checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const {
+
+ if (getData(entity)->car != car || getData(entity)->location != kLocationInsideCompartment)
+ return false;
+
+ EntityPosition entityPosition = getData(entity)->entityPosition;
+
+ // Test values
+ if (position == kPosition_4455) {
+ if (entityPosition == kPosition_4070 || entityPosition == kPosition_4455 || entityPosition == kPosition_4840)
+ return true;
+
+ return false;
+ }
+
+ if (position == kPosition_6130) {
+ if (entityPosition == kPosition_5790 || entityPosition == kPosition_6130 || entityPosition == kPosition_6470)
+ return true;
+
+ return false;
+ }
+
+ if (position != kPosition_7850
+ || (entityPosition != kPosition_7500 && entityPosition != kPosition_7850 && entityPosition != kPosition_8200))
+ return false;
+
+ return true;
+}
+
+bool Entities::isInBaggageCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarBaggage)
+ && getData(entity)->entityPosition >= kPosition_4500
+ && getData(entity)->entityPosition <= kPosition_5500;
+}
+
+bool Entities::isInBaggageCar(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarBaggage) && getData(entity)->entityPosition < kPosition_4500;
+}
+
+bool Entities::isInKronosSanctum(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos)
+ && getData(entity)->entityPosition >= kPosition_3500
+ && getData(entity)->entityPosition <= kPosition_5500;
+}
+
+bool Entities::isInKronosCarEntrance(EntityIndex entity) const {
+ return isInsideTrainCar(entity, kCarKronos) && getData(entity)->entityPosition > kPosition_7900;
+}
+
+bool Entities::checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const {
+ return distance >= ABS(getData(entity)->entityPosition - position);
+}
+
+bool Entities::isWalkingOppositeToPlayer(EntityIndex entity) const {
+ if (getData(entity)->direction == kDirectionUp && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ return true;
+
+ return (getData(entity)->direction == kDirectionDown && getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp));
+}
+
+bool Entities::isFemale(EntityIndex entity) {
+ return (entity == kEntityAnna
+ || entity == kEntityTatiana
+ || entity == kEntityVesna
+ || entity == kEntityKahina
+ || entity == kEntityMmeBoutarel
+ || entity == kEntityRebecca
+ || entity == kEntitySophie
+ || entity == kEntityYasmin
+ || entity == kEntityHadija
+ || entity == kEntityAlouan);
+}
+
+bool Entities::isMarried(EntityIndex entity) {
+ return (entity != kEntityTatiana
+ && entity != kEntityRebecca
+ && entity != kEntitySophie);
+}
+
+bool Entities::checkPosition(EntityPosition position) const {
+ Position position1 = 0;
+ Position position2 = 0;
+
+ switch (position) {
+ default:
+ return true;
+
+ case kPosition_1500:
+ position1 = 1;
+ position2 = 23;
+ break;
+
+ case kPosition_2740:
+ position1 = 3;
+ position2 = 25;
+ break;
+
+ case kPosition_3050:
+ position1 = 5;
+ position2 = 26;
+ break;
+
+ case kPosition_4070:
+ position1 = 7;
+ position2 = 28;
+ break;
+
+ case kPosition_4840:
+ position1 = 9;
+ position2 = 30;
+ break;
+
+ case kPosition_5790:
+ position1 = 11;
+ position2 = 32;
+ break;
+
+ case kPosition_6470:
+ position1 = 13;
+ position2 = 34;
+ break;
+
+ case kPosition_7500:
+ position1 = 15;
+ position2 = 36;
+ break;
+
+ case kPosition_8200:
+ position1 = 17;
+ position2 = 38;
+ break;
+ }
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp) && entityPositions[position1] >= getEntityData(kEntityPlayer)->entityPosition)
+ return true;
+ else
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) && entityPositions[position2] <= getEntityData(kEntityPlayer)->entityPosition);
+}
+
+bool Entities::checkSequenceFromPosition(EntityIndex entity) const {
+ FrameInfo *info = getEntityData(entity)->sequence->getFrameInfo((uint16)getEntityData(entity)->currentFrame);
+
+ if (getEntityData(entity)->direction == kDirectionUp)
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)
+ && info->entityPosition + getEntityPositionFromCurrentPosition() > kPosition_8513);
+
+ if (getEntityData(entity)->direction == kDirectionDown)
+ return (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown)
+ && info->entityPosition + getEntityPositionFromCurrentPosition() < kPosition_2087);
+
+ return false;
+}
+
+EntityPosition Entities::getEntityPositionFromCurrentPosition() const {
+ // Get the scene position first
+ Position position = getScenes()->get(getState()->scene)->position;
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp))
+ return (EntityPosition)(entityPositions[position] - kPosition_1430);
+
+ if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown))
+ return (EntityPosition)(entityPositions[position] - kPosition_9020);
+
+ return kPositionNone;
+}
+
+void Entities::clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const {
+ getScenes()->removeAndRedraw(&data->frame, false);
+ getScenes()->removeAndRedraw(&data->frame1, false);
+
+ SAFE_DELETE(data->sequence);
+ SAFE_DELETE(data->sequence2);
+
+ data->sequenceName = "";
+ data->sequenceName2 = "";
+
+ data->field_4A9 = false;
+ data->field_4AA = false;
+ data->directionSwitch = kDirectionNone;
+
+ data->currentFrame = -1;
+ data->currentFrame2 = 0;
+
+ data->direction = direction;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h
new file mode 100644
index 0000000000..8cc2072c42
--- /dev/null
+++ b/engines/lastexpress/game/entities.h
@@ -0,0 +1,380 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_ENTITIES_H
+#define LASTEXPRESS_ENTITIES_H
+
+/*
+ Entities
+ --------
+
+ The entities structure contains 40 Entity_t structures for each entity
+
+*/
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/rect.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+
+class Entities : Common::Serializable {
+public:
+ Entities(LastExpressEngine *engine);
+ ~Entities();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ void savePositions(Common::Serializer &ser);
+ void saveCompartments(Common::Serializer &ser);
+
+ void setup(bool isFirstChapter, EntityIndex entity);
+ void setupChapter(ChapterIndex chapter);
+ void reset();
+
+ // Update & drawing
+
+ /**
+ * Reset an entity state
+ *
+ * @param entity entity index
+ * @note remember to call the function pointer (we do not pass it our implementation)
+ */
+ void resetState(EntityIndex entity);
+ void updateFields() const;
+ void updateSequences() const;
+ void updateCallbacks();
+
+ EntityIndex canInteractWith(const Common::Point &point) const;
+ bool compare(EntityIndex entity1, EntityIndex entity2) const;
+
+ /**
+ * Update an entity current sequence frame (and related fields)
+ *
+ * @param entity entity index
+ */
+ void updateFrame(EntityIndex entity) const;
+ void updatePositionEnter(EntityIndex entity, CarIndex car, Position position);
+ void updatePositionExit(EntityIndex entity, CarIndex car, Position position);
+ void enterCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+ void exitCompartment(EntityIndex entity, ObjectIndex compartment, bool useCompartment1 = false);
+
+ // Sequences
+ void drawSequenceLeft(EntityIndex index, const char *sequence) const;
+ void drawSequenceRight(EntityIndex index, const char *sequence) const;
+ void clearSequences(EntityIndex index) const;
+
+ bool updateEntity(EntityIndex entity, CarIndex car, EntityPosition position) const;
+ bool hasValidFrame(EntityIndex entity) const;
+
+ // Accessors
+ Entity *get(EntityIndex entity);
+ EntityData::EntityCallData *getData(EntityIndex entity) const;
+ int getPosition(CarIndex car, Position position) const;
+ int getCompartments(int index) const;
+ int getCompartments1(int index) const;
+
+ // Scene
+ void loadSceneFromEntityPosition(CarIndex car, EntityPosition position, bool alternate = false) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Checks
+ //////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Query if 'entity' is inside a compartment
+ *
+ * @param entity The entity.
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if inside the compartment, false if not.
+ */
+ bool isInsideCompartment(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ bool checkFields2(ObjectIndex object) const;
+
+ /**
+ * Query if 'entity' is in compartment cars.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in compartment cars, false if not.
+ */
+ bool isInsideCompartments(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in the specified position
+ *
+ * @param car The car.
+ * @param position The position.
+ * @return true if player is in that position, false if not.
+ */
+ bool isPlayerPosition(CarIndex car, Position position) const;
+
+ /**
+ * Query if 'entity' is inside a train car
+ *
+ * @param entity The entity.
+ * @param car The car.
+ *
+ * @return true if inside a train car, false if not.
+ */
+ bool isInsideTrainCar(EntityIndex entity, CarIndex car) const;
+
+ /**
+ * Query if 'entity' is in green car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the green car entrance, false if not.
+ */
+ bool isInGreenCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if the player is in a specific car
+ *
+ * @param car The car.
+ *
+ * @return true if player is in the car, false if not.
+ */
+ bool isPlayerInCar(CarIndex car) const;
+
+ /**
+ * Query if 'entity' is going in the up or down direction.
+ *
+ * @param entity The entity.
+ *
+ * @return true if direction is up or down, false if not.
+ */
+ bool isDirectionUpOrDown(EntityIndex entity) const;
+
+ /**
+ * Query if the distance between the two entities is less 'distance'
+ *
+ * @param entity1 The first entity.
+ * @param entity2 The second entity.
+ * @param distance The distance.
+ *
+ * @return true if the distance between entities is less than 'distance', false if not.
+ */
+ bool isDistanceBetweenEntities(EntityIndex entity1, EntityIndex entity2, uint distance) const;
+
+ bool checkFields10(EntityIndex entity) const;
+
+ /**
+ * Query if there is somebody in the restaurant or salon.
+ *
+ * @return true if somebody is in the restaurant or salon, false if not.
+ */
+ bool isSomebodyInsideRestaurantOrSalon() const;
+
+ /**
+ * Query if 'entity' is in the salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the salon, false if not.
+ */
+ bool isInSalon(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the restaurant.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the restaurant, false if not.
+ */
+ bool isInRestaurant(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos salon.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos salon, false if not.
+ */
+ bool isInKronosSalon(EntityIndex entity) const;
+
+ /**
+ * Query if the player is outside Alexei window.
+ *
+ * @return true if outside alexei window, false if not.
+ */
+ bool isOutsideAlexeiWindow() const;
+
+ /**
+ * Query if the player is outside Anna window.
+ *
+ * @return true if outside anna window, false if not.
+ */
+ bool isOutsideAnnaWindow() const;
+
+ /**
+ * Query if 'entity' is in the kitchen.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the kitchen, false if not.
+ */
+ bool isInKitchen(EntityIndex entity) const;
+
+ /**
+ * Query if nobody is in a compartment at that position.
+ *
+ * @param car The car.
+ * @param position The position.
+ *
+ * @return true if nobody is in a compartment, false if not.
+ */
+ bool isNobodyInCompartment(CarIndex car, EntityPosition position) const;
+
+ bool checkFields19(EntityIndex entity, CarIndex car, EntityPosition position) const;
+
+ /**
+ * Query if 'entity' is in the baggage car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car entrance, false if not.
+ */
+ bool isInBaggageCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in the baggage car.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in the baggage car, false if not.
+ */
+ bool isInBaggageCar(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos sanctum.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos sanctum, false if not.
+ */
+ bool isInKronosSanctum(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is in Kronos car entrance.
+ *
+ * @param entity The entity.
+ *
+ * @return true if in Kronos car entrance, false if not.
+ */
+ bool isInKronosCarEntrance(EntityIndex entity) const;
+
+ /**
+ * Check distance from position.
+ *
+ * @param entity The entity.
+ * @param position The position.
+ * @param distance The distance.
+ *
+ * @return true if distance is bigger, false otherwise.
+ */
+ bool checkDistanceFromPosition(EntityIndex entity, EntityPosition position, int distance) const;
+
+ /**
+ * Query if 'entity' is walking opposite to player.
+ *
+ * @param entity The entity.
+ *
+ * @return true if walking opposite to player, false if not.
+ */
+ bool isWalkingOppositeToPlayer(EntityIndex entity) const;
+
+ /**
+ * Query if 'entity' is female.
+ *
+ * @param entity The entity.
+ *
+ * @return true if female, false if not.
+ */
+ static bool isFemale(EntityIndex entity);
+
+ /**
+ * Query if 'entity' is married.
+ *
+ * @param entity The entity.
+ *
+ * @return true if married, false if not.
+ */
+ static bool isMarried(EntityIndex entity);
+
+private:
+ static const int _compartmentsCount = 16;
+ static const int _positionsCount = 100 * 10; // 100 positions per train car
+
+ LastExpressEngine *_engine;
+ EntityData *_header;
+ Common::Array<Entity *> _entities;
+
+ // Compartments & positions
+ uint _compartments[_compartmentsCount];
+ uint _compartments1[_compartmentsCount];
+ uint _positions[_positionsCount];
+
+ void executeCallbacks();
+ void processEntity(EntityIndex entity);
+
+ void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const;
+ void drawSequences(EntityIndex entity, EntityDirection direction, bool loadSequence) const;
+ void loadSequence2(EntityIndex entity, Common::String sequenceName, Common::String sequenceName2, byte field30, bool loadSequence) const;
+
+ void clearEntitySequenceData(EntityData::EntityCallData *data, EntityDirection direction) const;
+ void computeCurrentFrame(EntityIndex entity) const;
+ int16 getCurrentFrame(EntityIndex entity, Sequence *sequence, EntityPosition position, bool doProcessing) const;
+ void processFrame(EntityIndex entity, bool keepPreviousFrame, bool dontPlaySound);
+ void drawNextSequence(EntityIndex entity) const;
+ void updateEntityPosition(EntityIndex entity) const;
+ void copySequenceData(EntityIndex entity) const;
+
+ bool changeCar(EntityData::EntityCallData *data, EntityIndex entity, CarIndex car, EntityPosition position, bool increment, EntityPosition newPosition, CarIndex newCar) const;
+
+ void getSequenceName(EntityIndex entity, EntityDirection direction, Common::String &sequence1, Common::String &sequence2) const;
+
+ void updatePositionsEnter(EntityIndex entity, CarIndex car, Position position1, Position position2, Position position3, Position position4);
+ void updatePositionsExit(EntityIndex entity, CarIndex car, Position position1, Position position2);
+
+ void resetSequences(EntityIndex entity) const;
+
+ bool checkPosition(EntityPosition position) const;
+ bool checkSequenceFromPosition(EntityIndex entity) const;
+ EntityPosition getEntityPositionFromCurrentPosition() const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_ENTITIES_H
diff --git a/engines/lastexpress/game/fight.cpp b/engines/lastexpress/game/fight.cpp
new file mode 100644
index 0000000000..8fa711df1c
--- /dev/null
+++ b/engines/lastexpress/game/fight.cpp
@@ -0,0 +1,1587 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/fight.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+#define CALL_FUNCTION0(fighter, name) \
+ (*fighter->name)(fighter)
+
+#define CALL_FUNCTION1(fighter, name, a) \
+ (*fighter->name)(fighter, a)
+
+#define REGISTER_PLAYER_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##namePlayer - invalid data!"); \
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction##name); \
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update##name); \
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract##name);
+
+#define REGISTER_OPPONENT_FUNCTIONS(name) \
+ if (!_data) \
+ error("Fight::load##nameOpponent - invalid data!"); \
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleOpponentAction##name); \
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponent##name); \
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+#define CHECK_SEQUENCE2(fighter, value) \
+ (fighter->frame->getInfo()->field_33 & value)
+
+Fight::Fight(LastExpressEngine *engine) : _engine(engine), _data(NULL), _endType(kFightEndLost), _state(0), _handleTimer(false) {}
+
+Fight::~Fight() {
+ clearData();
+ _data = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::eventMouse(const Common::Event &ev) {
+ if (!_data || _data->index)
+ return;
+
+ // TODO move all the egg handling to inventory functions
+
+ getFlags()->mouseLeftClick = false;
+ getFlags()->shouldRedraw = false;
+ getFlags()->mouseRightClick = false;
+
+ if (ev.mouse.x < 608 || ev.mouse.y < 448 || ev.mouse.x >= 640 || ev.mouse.x >= 480) {
+
+ // Handle right button click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ getSound()->removeFromQueue(kEntityTables0);
+ setStopped();
+
+ getGlobalTimer() ? _state = 0 : ++_state;
+
+ getFlags()->mouseRightClick = true;
+ }
+
+ if (_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = false;
+ }
+
+ // Check hotspots
+ Scene *scene = getScenes()->get(getState()->scene);
+ SceneHotspot *hotspot = NULL;
+
+ if (!scene->checkHotSpot(ev.mouse, &hotspot)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+
+ // Call player function
+ if (CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ CALL_FUNCTION1(_data->player, handleAction, (FightAction)hotspot->action);
+ } else {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ }
+ }
+ } else {
+ // Handle clicks on menu icon
+
+ if (!_handleTimer) {
+ // Timer expired => show with full brightness
+ if (!getGlobalTimer())
+ getInventory()->drawEgg();
+
+ _handleTimer = true;
+ }
+
+ // Stop fight if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ _handleTimer = false;
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndExit);
+ }
+
+ // Reset timer on right click
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ if (getGlobalTimer()) {
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ setGlobalTimer(900);
+ }
+ }
+ }
+
+ getFlags()->shouldRedraw = true;
+}
+
+void Fight::eventTick(const Common::Event &ev) {
+ handleTick(ev, true);
+}
+
+void Fight::handleTick(const Common::Event &ev, bool isProcessing) {
+ // TODO move all the egg handling to inventory functions
+
+ // Blink egg
+ if (getGlobalTimer()) {
+ warning("Fight::handleMouseMove - egg blinking not implemented!");
+ }
+
+ if (!_data || _data->index)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ if (!getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot) || !CALL_FUNCTION1(_data->player, canInteract, (FightAction)hotspot->action)) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ } else {
+ _engine->getCursor()->setStyle((CursorStyle)hotspot->cursor);
+ }
+
+ CALL_FUNCTION0(_data->player, update);
+ CALL_FUNCTION0(_data->opponent, update);
+
+ // Draw sequences
+ if (!_data->isRunning)
+ return;
+
+ if (isProcessing)
+ getScenes()->drawFrames(true);
+
+ if (_data->index) {
+ // Set next sequence name index
+ _data->index--;
+ _data->sequences[_data->index] = loadSequence(_data->names[_data->index]);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+//////////////////////////////////////////////////////////////////////////
+
+Fight::FightEndType Fight::setup(FightType type) {
+ if (_data)
+ error("Fight::setup - calling fight setup again while a fight is already in progress!");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Prepare UI & state
+ if (_state >= 5 && (type == kFightSalko || type == kFightVesna)) {
+ _state = 0;
+ return kFightEndWin;
+ }
+
+ getInventory()->showHourGlass();
+ // TODO events function
+ getFlags()->flag_0 = false;
+ getFlags()->mouseRightClick = false;
+ getEntities()->reset();
+
+ // Compute scene to use
+ SceneIndex sceneIndex;
+ switch(type) {
+ default:
+ sceneIndex = kSceneFightDefault;
+ break;
+
+ case kFightMilos:
+ sceneIndex = (getObjects()->get(kObjectCompartment1).location2 < kObjectLocation3) ? kSceneFightMilos : kSceneFightMilosBedOpened;
+ break;
+
+ case kFightAnna:
+ sceneIndex = kSceneFightAnna;
+ break;
+
+ case kFightIvo:
+ sceneIndex = kSceneFightIvo;
+ break;
+
+ case kFightSalko:
+ sceneIndex = kSceneFightSalko;
+ break;
+
+ case kFightVesna:
+ sceneIndex = kSceneFightVesna;
+ break;
+ }
+
+ if (getFlags()->shouldRedraw) {
+ getFlags()->shouldRedraw = false;
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Load the scene object
+ Scene *scene = getScenes()->get(sceneIndex);
+
+ // Update game entities and state
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->location = scene->location;
+
+ getState()->scene = sceneIndex;
+
+ getFlags()->flag_3 = true;
+
+ // Draw the scene
+ _engine->getGraphicsManager()->draw(scene, GraphicsManager::kBackgroundC);
+ // FIXME move to start of fight?
+ askForRedraw();
+ redrawScreen();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Setup the fight
+ _data = new FightData;
+ loadData(type);
+
+ // Show opponents & egg button
+ Common::Event emptyEvent;
+ handleTick(emptyEvent, false);
+ getInventory()->drawEgg();
+
+ // Start fight
+ _endType = kFightEndLost;
+ while (_data->isRunning) {
+ if (_engine->handleEvents())
+ continue;
+
+ getSound()->updateQueue();
+ }
+
+ // Cleanup after fight is over
+ clearData();
+
+ return _endType;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Status
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setStopped() {
+ if (_data)
+ _data->isRunning = false;
+}
+
+void Fight::bailout(FightEndType type) {
+ _state = 0;
+ _endType = type;
+ setStopped();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Cleanup
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::clearData() {
+ if (!_data)
+ return;
+
+ // Clear data
+ clearSequences(_data->player);
+ clearSequences(_data->opponent);
+
+ delete _data->player;
+ delete _data->opponent;
+
+ delete _data;
+ _data = NULL;
+
+ _engine->restoreEventHandlers();
+}
+
+void Fight::clearSequences(Fighter *combatant) const {
+ if (!combatant)
+ return;
+
+ // The original game resets the function pointers to default values, just before deleting the struct
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ // Free sequences
+ for (int i = 0; i < (int)combatant->sequences.size(); i++)
+ delete combatant->sequences[i];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::setSequenceAndDraw(Fighter *combatant, uint32 sequenceIndex, FightSequenceType type) const {
+ if (combatant->sequences.size() < sequenceIndex)
+ return;
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightSequenceType0:
+ if (combatant->sequenceIndex)
+ return;
+
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType1:
+ combatant->sequence = combatant->sequences[sequenceIndex];
+ combatant->sequenceIndex = sequenceIndex;
+ combatant->sequenceIndex2 = 0;
+ draw(combatant);
+ break;
+
+ case kFightSequenceType2:
+ combatant->sequenceIndex2 = sequenceIndex;
+ break;
+ }
+}
+
+void Fight::draw(Fighter *combatant) const {
+ getScenes()->removeAndRedraw(&combatant->frame, false);
+
+ combatant->frameIndex = 0;
+ combatant->field_24 = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Loading
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadData(FightType type) {
+ if (!_data)
+ error("Fight::loadData - invalid data!");
+
+ switch (type) {
+ default:
+ break;
+
+ case kFightMilos:
+ loadMilosPlayer();
+ loadMilosOpponent();
+ break;
+
+ case kFightAnna:
+ loadAnnaPlayer();
+ loadAnnaOpponent();
+ break;
+
+ case kFightIvo:
+ loadIvoPlayer();
+ loadIvoOpponent();
+ break;
+
+ case kFightSalko:
+ loadSalkoPlayer();
+ loadSalkoOpponent();
+ break;
+
+ case kFightVesna:
+ loadVesnaPlayer();
+ loadVesnaOpponent();
+ break;
+ }
+
+ if (!_data->player || !_data->opponent)
+ error("Fight::loadData - error loading fight data (type=%d)", type);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Start running the fight
+ _data->isRunning = true;
+
+ if (_state < 5) {
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ goto end_load;
+ }
+
+ switch(type) {
+ default:
+ break;
+
+ case kFightMilos:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 4, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 0, kFightSequenceType0);
+ break;
+
+ case kFightIvo:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType0);
+ setSequenceAndDraw(_data->opponent, 6, kFightSequenceType0);
+ break;
+
+ case kFightVesna:
+ _data->opponent->countdown = 1;
+ setSequenceAndDraw(_data->player, 0, kFightSequenceType0);
+ setSequenceAndDraw(_data->player, 3, kFightSequenceType2);
+ setSequenceAndDraw(_data->opponent, 5, kFightSequenceType0);
+ break;
+ }
+
+end_load:
+ // Setup event handlers
+ _engine->backupEventHandlers();
+ SET_EVENT_HANDLERS(Fight, this);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Shared
+//////////////////////////////////////////////////////////////////////////
+void Fight::processFighter(Fighter *fighter) {
+ if (!_data)
+ error("Fight::processFighter - invalid data!");
+
+ if (!fighter->sequence) {
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+ getScenes()->setCoordinates(fighter->frame);
+ }
+ SAFE_DELETE(fighter->frame);
+ return;
+ }
+
+ if (fighter->sequence->count() <= fighter->frameIndex) {
+ switch(fighter->action) {
+ default:
+ break;
+
+ case kFightAction101:
+ setSequenceAndDraw(fighter, fighter->sequenceIndex2, kFightSequenceType1);
+ fighter->sequenceIndex2 = 0;
+ break;
+
+ case kFightActionResetFrame:
+ fighter->frameIndex = 0;
+ break;
+
+ case kFightAction103:
+ setSequenceAndDraw(fighter, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter, handleAction, kFightAction101);
+ setSequenceAndDraw(fighter->opponent, 0, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction101);
+ CALL_FUNCTION0(fighter->opponent, update);
+ break;
+
+ case kFightActionWin:
+ bailout(kFightEndWin);
+ break;
+
+ case kFightActionLost:
+ bailout(kFightEndLost);
+ break;
+ }
+ }
+
+ if (_data->isRunning) {
+
+ // Get the current sequence frame
+ SequenceFrame *frame = new SequenceFrame(fighter->sequence, (uint16)fighter->frameIndex);
+ frame->getInfo()->location = 1;
+
+ if (fighter->frame == frame) {
+ delete frame;
+ return;
+ }
+
+ getSound()->playFightSound(frame->getInfo()->soundAction, frame->getInfo()->field_31);
+
+ // Add current frame to queue and advance
+ getScenes()->addToQueue(frame);
+ fighter->frameIndex++;
+
+ if (fighter->frame) {
+ getScenes()->removeFromQueue(fighter->frame);
+
+ if (!frame->getInfo()->field_2E)
+ getScenes()->setCoordinates(fighter->frame);
+ }
+
+ // Replace by new frame
+ delete fighter->frame;
+ fighter->frame = frame;
+ }
+}
+
+void Fight::handleAction(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ return;
+
+ case kFightAction101:
+ break;
+
+ case kFightActionResetFrame:
+ fighter->countdown--;
+ break;
+
+ case kFightAction103:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionWin:
+ _endType = kFightEndWin;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+
+ case kFightActionLost:
+ _endType = kFightEndLost;
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightActionResetFrame);
+ break;
+ }
+
+ // Update action
+ fighter->action = action;
+}
+
+bool Fight::canInteract(Fighter const *fighter, FightAction /*= (FightAction)0*/ ) {
+ return (fighter->action == kFightAction101 && !fighter->sequenceIndex);
+}
+
+void Fight::update(Fighter *fighter) {
+
+ processFighter(fighter);
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = (fighter->action == kFightActionResetFrame ? 2 : 0);
+}
+
+void Fight::updateOpponent(Fighter *fighter) {
+
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ processFighter(opponent);
+
+ if (opponent->field_38 && !opponent->sequenceIndex)
+ opponent->field_38--;
+
+ if (fighter->frame)
+ fighter->frame->getInfo()->location = 1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Milos
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadMilosPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Milos)
+
+ _data->player->sequences.push_back(loadSequence("2001cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgr.seq"));
+ _data->player->sequences.push_back(loadSequence("2001csgl.seq"));
+ _data->player->sequences.push_back(loadSequence("2001dbk.seq"));
+}
+
+void Fight::loadMilosOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Milos)
+
+ _data->opponent->sequences.push_back(loadSequence("2001or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001okm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001dbk.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2001wbk.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS027", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 35;
+}
+
+void Fight::handleActionMilos(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 3, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 6, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4) || fighter->opponent->sequenceIndex != 1) {
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, rnd(3) + 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+ }
+}
+
+void Fight::updateMilos(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ getSound()->removeFromQueue(kEntityTables0);
+ getSound()->playSound(kEntityTrain, "MUS029", SoundManager::kFlagDefault);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ }
+
+ if (fighter->sequenceIndex == 4) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction4);
+ _endType = kFightEndLost;
+ }
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractMilos(Fighter const *fighter, FightAction action) {
+ if (!_data)
+ error("Fight::canInteractMilos - invalid data!");
+
+ if (action != kFightAction128
+ || _data->player->sequenceIndex != 1
+ || !fighter->frame
+ || CHECK_SEQUENCE2(fighter, 4)
+ || fighter->opponent->sequenceIndex != 1) {
+ return canInteract(fighter);
+ }
+
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+}
+
+void Fight::handleOpponentActionMilos(Fighter *fighter, FightAction action) {
+ if (action == kFightAction4) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ if (action != kFightAction131)
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentMilos(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType1);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ } else {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ }
+
+ // Update field_38
+ if (opponent->opponent->field_34 < 5)
+ opponent->field_38 = 6 * (5 - opponent->opponent->field_34);
+ else
+ opponent->field_38 = 0;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Anna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadAnnaPlayer() {
+ if (!_data)
+ error("Fight::loadAnnaPlayer - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->player->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleActionAnna);
+ _data->player->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::update);
+ _data->player->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->player->sequences.push_back(loadSequence("2002cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdl.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2002cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2002lbk.seq"));
+}
+
+void Fight::loadAnnaOpponent() {
+ if (!_data)
+ error("Fight::loadAnnaOpponent - invalid data!");
+
+ // Special case: we are using some shared functions directly
+ _data->opponent->handleAction = new Common::Functor2Mem<Fighter *, FightAction, void, Fight>(this, &Fight::handleAction);
+ _data->opponent->update = new Common::Functor1Mem<Fighter *, void, Fight>(this, &Fight::updateOpponentAnna);
+ _data->opponent->canInteract = new Common::Functor2Mem<Fighter const *, FightAction, bool, Fight>(this, &Fight::canInteract);
+
+ _data->opponent->sequences.push_back(loadSequence("2002or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2002okm.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS030", SoundManager::kFlagDefault);
+
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionAnna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if ((fighter->sequenceIndex != 1 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction3:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 1) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 4, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 6, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+ }
+
+ if (fighter->field_34 > 4) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ }
+}
+
+void Fight::updateOpponentAnna(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = (int32)rnd(15);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 3)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Ivo
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadIvoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Ivo)
+
+ _data->player->sequences.push_back(loadSequence("2003cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003car.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cal.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003cdm.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003chl.seq"));
+ _data->player->sequences.push_back(loadSequence("2003ckr.seq"));
+ _data->player->sequences.push_back(loadSequence("2003lbk.seq"));
+ _data->player->sequences.push_back(loadSequence("2003fbk.seq"));
+
+ _data->player->countdown = 5;
+}
+
+void Fight::loadIvoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Ivo)
+
+ _data->opponent->sequences.push_back(loadSequence("2003or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oal.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003odm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okl.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003okj.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+ _data->opponent->sequences.push_back(loadSequence("csdr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2003l.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS032", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 5;
+ _data->opponent->field_38 = 15;
+}
+
+void Fight::handleActionIvo(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1 || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction2:
+ if ((fighter->sequenceIndex != 2 && fighter->sequenceIndex != 3) || CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ switch (fighter->opponent->sequenceIndex) {
+ default:
+ case 1:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+ break;
+
+ case kFightAction129:
+ setSequenceAndDraw(fighter, (fighter->opponent->countdown > 1) ? 4 : 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+
+ case kFightAction130:
+ setSequenceAndDraw(fighter, 3, fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0);
+ break;
+ }
+}
+
+void Fight::updateIvo(Fighter *fighter) {
+
+ if ((fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4) && !fighter->frameIndex)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction131);
+
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ // Draw sequences
+ if (fighter->opponent->countdown <= 0) {
+ setSequenceAndDraw(fighter, 9, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(fighter, handleAction, kFightActionWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 3 || fighter->sequenceIndex == 4)
+ CALL_FUNCTION1(fighter->opponent, handleAction, (FightAction)fighter->sequenceIndex);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractIvo(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction129 || action == kFightAction130)
+ return (fighter->sequenceIndex >= 8);
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionIvo(Fighter *fighter, FightAction action) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ if ((opponent->sequenceIndex != 1 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 6, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction4:
+ if ((opponent->sequenceIndex != 2 && opponent->sequenceIndex != 3) || CHECK_SEQUENCE2(opponent, 4)) {
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 5, kFightSequenceType1);
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightAction103);
+ }
+ break;
+
+ case kFightAction131:
+ if (opponent->sequenceIndex)
+ break;
+
+ if (rnd(100) <= (unsigned int)(opponent->countdown > 2 ? 60 : 75)) {
+ setSequenceAndDraw(opponent, 3 , kFightSequenceType1);
+ if (opponent->opponent->sequenceIndex == 4)
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ }
+ break;
+ }
+}
+
+void Fight::updateOpponentIvo(Fighter *fighter) {
+ // This is an opponent struct!
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 >= 2) {
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType2);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 0, kFightSequenceType1);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 3 * opponent->countdown + (int32)rnd(10);
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+
+ if (opponent->opponent->countdown <= 0) {
+ setSequenceAndDraw(opponent, 7, kFightSequenceType1);
+ setSequenceAndDraw(opponent->opponent, 8, kFightSequenceType1);
+ getSound()->removeFromQueue(kEntityTables0);
+
+ CALL_FUNCTION1(opponent->opponent, handleAction, kFightActionWin);
+
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Salko
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadSalkoPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Salko)
+
+ _data->player->sequences.push_back(loadSequence("2004cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2004chj.seq"));
+ _data->player->sequences.push_back(loadSequence("2004bk.seq"));
+
+ _data->player->countdown = 2;
+}
+
+void Fight::loadSalkoOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Salko)
+
+ _data->opponent->sequences.push_back(loadSequence("2004or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2004ohm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("blank.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS035", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 3;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionSalko(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ case kFightAction2:
+ if (fighter->sequenceIndex != 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ fighter->field_34 = 0;
+
+ setSequenceAndDraw(fighter, 3, kFightSequenceType1);
+ setSequenceAndDraw(fighter->opponent, (action == kFightAction1 ? 3 : 4), kFightSequenceType1);
+
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+
+ if (action == kFightAction2)
+ fighter->countdown= 0;
+
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ setSequenceAndDraw(fighter, 1, kFightSequenceType0);
+ fighter->field_34 = 0;
+ break;
+
+ case kFightAction131:
+ setSequenceAndDraw(fighter, 2, (fighter->sequenceIndex ? kFightSequenceType2 : kFightSequenceType0));
+ break;
+ }
+}
+
+void Fight::updateSalko(Fighter *fighter) {
+ update(fighter);
+
+ // The original doesn't check for currentSequence2 != NULL (might not happen when everything is working properly, but crashes with our current implementation)
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+
+ return;
+ }
+
+ if (fighter->sequenceIndex == 2)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction2);
+ }
+}
+
+bool Fight::canInteractSalko(Fighter const *fighter, FightAction action) {
+ if (action == kFightAction131) {
+ if (fighter->sequenceIndex == 1) {
+ if (fighter->opponent->countdown <= 0)
+ _engine->getCursor()->setStyle(kCursorHand);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ return canInteract(fighter);
+}
+
+void Fight::handleOpponentActionSalko(Fighter *fighter, FightAction action) {
+ if (action == kFightAction2) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ } else {
+ handleAction(fighter, action);
+ }
+}
+
+void Fight::updateOpponentSalko(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ switch (rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndLost);
+
+ // Stop processing
+ return;
+ }
+
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+ }
+
+ updateOpponent(opponent);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Vesna
+//////////////////////////////////////////////////////////////////////////
+
+void Fight::loadVesnaPlayer() {
+ REGISTER_PLAYER_FUNCTIONS(Vesna)
+
+ _data->player->sequences.push_back(loadSequence("2005cr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cbr.seq"));
+ _data->player->sequences.push_back(loadSequence("2005bk.seq"));
+ _data->player->sequences.push_back(loadSequence("2005cdm1.seq"));
+ _data->player->sequences.push_back(loadSequence("2005chl.seq"));
+}
+
+void Fight::loadVesnaOpponent() {
+ REGISTER_OPPONENT_FUNCTIONS(Vesna)
+
+ _data->opponent->sequences.push_back(loadSequence("2005or.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oar.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okml.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005okr.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005odm1.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005csbm.seq"));
+ _data->opponent->sequences.push_back(loadSequence("2005oam4.seq"));
+
+ getSound()->playSound(kEntityTables0, "MUS038", SoundManager::kFlagDefault);
+
+ _data->opponent->countdown = 4;
+ _data->opponent->field_38 = 30;
+}
+
+void Fight::handleActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ return;
+
+ case kFightAction1:
+ if (fighter->sequenceIndex != 1) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction2:
+ if (fighter->sequenceIndex != 2) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ } else {
+ fighter->field_34++;
+ }
+ break;
+
+ case kFightAction5:
+ if (fighter->sequenceIndex != 3) {
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ CALL_FUNCTION0(fighter, update);
+ }
+ break;
+
+ case kFightAction128:
+ if (fighter->sequenceIndex == 1 && fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ setSequenceAndDraw(fighter, 5, kFightSequenceType1);
+ } else {
+ setSequenceAndDraw(fighter, (fighter->opponent->sequenceIndex == 5) ? 3 : 1, kFightSequenceType0);
+ }
+ break;
+
+ case kFightAction132:
+ setSequenceAndDraw(fighter, 2, kFightSequenceType0);
+ break;
+ }
+
+ if (fighter->field_34 > 10) {
+ setSequenceAndDraw(fighter->opponent, 5, kFightSequenceType2);
+ fighter->opponent->countdown = 1;
+ fighter->field_34 = 0;
+ }
+}
+
+void Fight::updateVesna(Fighter *fighter) {
+ if (fighter->frame && CHECK_SEQUENCE2(fighter, 2)) {
+
+ if (fighter->sequenceIndex == 3)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction3);
+
+ if (fighter->opponent->countdown <= 0) {
+ getSound()->removeFromQueue(kEntityTables0);
+ bailout(kFightEndWin);
+ return;
+ }
+
+ if (fighter->sequenceIndex == 5)
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction5);
+ }
+
+ update(fighter);
+}
+
+bool Fight::canInteractVesna(Fighter const *fighter, FightAction action) {
+ if (action != kFightAction128)
+ return canInteract(fighter);
+
+ if (fighter->sequenceIndex != 1) {
+
+ if (fighter->opponent->sequenceIndex == 5) {
+ _engine->getCursor()->setStyle(kCursorDown);
+ return true;
+ }
+
+ return canInteract(fighter);
+ }
+
+ if (fighter->opponent->sequenceIndex == 1 && CHECK_SEQUENCE2(fighter, 4)) {
+ _engine->getCursor()->setStyle(kCursorPunchLeft);
+ return true;
+ }
+
+ return false;
+}
+
+void Fight::handleOpponentActionVesna(Fighter *fighter, FightAction action) {
+ switch (action) {
+ default:
+ handleAction(fighter, action);
+ break;
+
+ case kFightAction3:
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ break;
+
+ case kFightAction5:
+ setSequenceAndDraw(fighter, 7, kFightSequenceType1);
+ CALL_FUNCTION1(fighter->opponent, handleAction, kFightAction103);
+ if (fighter->countdown <= 1)
+ fighter->countdown = 1;
+ break;
+
+ case kFightAction131:
+ break;
+ }
+}
+
+void Fight::updateOpponentVesna(Fighter *fighter) {
+ // This is an opponent struct
+ Opponent *opponent = (Opponent *)fighter;
+
+ if (!opponent->field_38 && CALL_FUNCTION1(opponent, canInteract, kFightAction1) && !opponent->sequenceIndex2) {
+
+ if (opponent->opponent->field_34 == 1) {
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ } else {
+ switch (rnd(6)) {
+ default:
+ break;
+
+ case 0:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ break;
+
+ case 3:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 4:
+ setSequenceAndDraw(opponent, 1, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 2, kFightSequenceType2);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 2, kFightSequenceType0);
+ setSequenceAndDraw(opponent, 1, kFightSequenceType2);
+ break;
+ }
+ }
+
+ // Update field_38
+ opponent->field_38 = 4 * opponent->countdown;
+ }
+
+ if (opponent->frame && CHECK_SEQUENCE2(opponent, 2)) {
+ if (opponent->sequenceIndex == 1 || opponent->sequenceIndex == 2 || opponent->sequenceIndex == 5)
+ CALL_FUNCTION1(opponent->opponent, handleAction, (FightAction)opponent->sequenceIndex);
+
+ if (opponent->opponent->countdown <= 0) {
+
+ switch (opponent->sequenceIndex) {
+ default:
+ break;
+
+ case 1:
+ setSequenceAndDraw(opponent, 3, kFightSequenceType1);
+ break;
+
+ case 2:
+ setSequenceAndDraw(opponent, 4, kFightSequenceType1);
+ break;
+
+ case 5:
+ setSequenceAndDraw(opponent, 6, kFightSequenceType1);
+ break;
+ }
+
+ setSequenceAndDraw(opponent->opponent, 4, kFightSequenceType1);
+
+ CALL_FUNCTION1(opponent, handleAction, kFightActionLost);
+ CALL_FUNCTION0(opponent->opponent, update);
+ CALL_FUNCTION0(opponent, update);
+
+ getSound()->removeFromQueue(kEntityTables0);
+
+ // Stop processing
+ return;
+ }
+ }
+
+ updateOpponent(opponent);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/fight.h b/engines/lastexpress/game/fight.h
new file mode 100644
index 0000000000..4484017184
--- /dev/null
+++ b/engines/lastexpress/game/fight.h
@@ -0,0 +1,269 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_FIGHT_H
+#define LASTEXPRESS_FIGHT_H
+
+/*
+ Fight structure
+ ---------------
+ uint32 {4} - player struct
+ uint32 {4} - opponent struct
+ uint32 {4} - hasLost flag
+
+ byte {1} - isRunning
+
+ Fight participant structure
+ ---------------------------
+ uint32 {4} - function pointer
+ uint32 {4} - pointer to fight structure
+ uint32 {4} - pointer to opponent (fight participant structure)
+ uint32 {4} - array of sequences
+ uint32 {4} - number of sequences
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint16 {2} - ??
+ uint16 {2} - ?? - only for opponent structure
+ uint32 {4} - ?? - only for opponent structure
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/array.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Sequence;
+class SequenceFrame;
+
+//////////////////////////////////////////////////////////////////////////
+// TODO : objectify!
+class Fight : public EventHandler {
+public:
+ enum FightEndType {
+ kFightEndWin = 0,
+ kFightEndLost = 1,
+ kFightEndExit = 2
+ };
+
+ Fight(LastExpressEngine *engine);
+ ~Fight();
+
+ FightEndType setup(FightType type);
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void setStopped();
+ void resetState() { _state = 0; }
+
+private:
+ enum FightSequenceType {
+ kFightSequenceType0 = 0,
+ kFightSequenceType1 = 1,
+ kFightSequenceType2 = 2
+ };
+
+ enum FightAction {
+ kFightAction1 = 1,
+ kFightAction2 = 2,
+ kFightAction3 = 3,
+ kFightAction4 = 4,
+ kFightAction5 = 5,
+ kFightAction101 = 101,
+ kFightActionResetFrame = 102,
+ kFightAction103 = 103,
+ kFightActionWin = 104,
+ kFightActionLost = 105,
+ kFightAction128 = 128,
+ kFightAction129 = 129,
+ kFightAction130 = 130,
+ kFightAction131 = 131,
+ kFightAction132 = 132
+ };
+
+ struct Fighter {
+ Common::Functor2<Fighter *, FightAction, void> *handleAction;
+ Common::Functor1<Fighter *, void> *update;
+ Common::Functor2<Fighter const *, FightAction, bool> *canInteract;
+ Fighter *opponent;
+ Common::Array<Sequence *> sequences;
+ uint32 sequenceIndex;
+ Sequence *sequence;
+ SequenceFrame *frame;
+ uint32 frameIndex;
+ uint32 field_24;
+ FightAction action;
+ uint32 sequenceIndex2;
+ int32 countdown; // countdown before loosing ?
+ uint32 field_34;
+
+ Fighter() {
+ handleAction = NULL;
+ update = NULL;
+ canInteract = NULL;
+
+ opponent = NULL;
+
+ sequenceIndex = 0;
+ sequence = NULL;
+ frame = NULL;
+ frameIndex = 0;
+
+ field_24 = 0;
+
+ action = kFightAction101;
+ sequenceIndex2 = 0;
+
+ countdown = 1;
+
+ field_34 = 0;
+ }
+ };
+
+ // Opponent struct
+ struct Opponent : Fighter {
+ int32 field_38;
+
+ Opponent() : Fighter() {
+ field_38 = 0;
+ }
+ };
+
+ struct FightData {
+ Fighter *player;
+ Opponent *opponent;
+ int32 index;
+
+ Sequence *sequences[20];
+ Common::String names[20];
+
+ bool isRunning;
+
+ FightData() {
+ player = new Fighter();
+ opponent = new Opponent();
+
+ // Set opponents
+ player->opponent = opponent;
+ opponent->opponent = player;
+
+ index = 0;
+
+ isRunning = false;
+ }
+ };
+
+ LastExpressEngine *_engine;
+ FightData *_data;
+ FightEndType _endType;
+ int _state;
+
+ bool _handleTimer;
+
+ // Events
+ void handleTick(const Common::Event &ev, bool unknown);
+
+ // State
+ void bailout(FightEndType type);
+
+
+ // Drawing
+ void setSequenceAndDraw(Fighter *fighter, uint32 sequenceIndex, FightSequenceType type) const;
+ void draw(Fighter *fighter) const;
+
+ // Cleanup
+ void clearData();
+ void clearSequences(Fighter *fighter) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Loading
+ void loadData(FightType type);
+
+ // Shared
+ void processFighter(Fighter *fighter);
+
+ // Default functions
+ void handleAction(Fighter *fighter, FightAction action);
+ void update(Fighter *fighter);
+ bool canInteract(Fighter const *fighter, FightAction = (FightAction)0);
+ void updateOpponent(Fighter *fighter);
+
+ // Milos
+ void loadMilosPlayer();
+ void loadMilosOpponent();
+ void handleActionMilos(Fighter *fighter, FightAction action);
+ void updateMilos(Fighter *fighter);
+ bool canInteractMilos(Fighter const *fighter, FightAction action);
+ void handleOpponentActionMilos(Fighter *fighter, FightAction action);
+ void updateOpponentMilos(Fighter *fighter);
+
+ // Anna
+ void loadAnnaPlayer();
+ void loadAnnaOpponent();
+ void handleActionAnna(Fighter *fighter, FightAction action);
+ void updateOpponentAnna(Fighter *fighter);
+
+ // Ivo
+ void loadIvoPlayer();
+ void loadIvoOpponent();
+ void handleActionIvo(Fighter *fighter, FightAction action);
+ void updateIvo(Fighter *fighter);
+ bool canInteractIvo(Fighter const *fighter, FightAction action);
+ void handleOpponentActionIvo(Fighter *fighter, FightAction action);
+ void updateOpponentIvo(Fighter *fighter);
+
+ // Salko
+ void loadSalkoPlayer();
+ void loadSalkoOpponent();
+ void handleActionSalko(Fighter *fighter, FightAction action);
+ void updateSalko(Fighter *fighter);
+ bool canInteractSalko(Fighter const *fighter, FightAction action);
+ void handleOpponentActionSalko(Fighter *fighter, FightAction action);
+ void updateOpponentSalko(Fighter *fighter);
+
+ // Vesna
+ void loadVesnaPlayer();
+ void loadVesnaOpponent();
+ void handleActionVesna(Fighter *fighter, FightAction action);
+ void updateVesna(Fighter *fighter);
+ bool canInteractVesna(Fighter const *fighter, FightAction action);
+ void handleOpponentActionVesna(Fighter *fighter, FightAction action);
+ void updateOpponentVesna(Fighter *fighter);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_FIGHT_H
diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp
new file mode 100644
index 0000000000..0223176cb6
--- /dev/null
+++ b/engines/lastexpress/game/inventory.cpp
@@ -0,0 +1,599 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/inventory.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/scene.h"
+#include "lastexpress/data/snd.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+
+#define drawItem(x, y, index, brightness) { Icon icon((CursorStyle)(index)); icon.setPosition(x, y); icon.setBrightness(brightness); _engine->getGraphicsManager()->draw(&icon, GraphicsManager::kBackgroundInventory); }
+
+namespace LastExpress {
+
+Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItem(kItemNone), _opened(false), _visible(false),
+ _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(100),
+ _flagUseMagnifier(false), _flag1(false), _flag2(false), _flagEggHightlighted(false), _itemScene(NULL) {
+
+ _inventoryRect = Common::Rect(0, 0, 32, 32);
+ _menuRect = Common::Rect(608, 448, 640, 480);
+ _selectedRect = Common::Rect(44, 0, 76, 32);
+
+ init();
+}
+
+Inventory::~Inventory() {
+ _itemScene = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Inventory handling
+//////////////////////////////////////////////////////////////////////////
+
+// Initialize inventory contents
+void Inventory::init() {
+ // ID
+ _entries[kItemMatchBox].cursor = kCursorMatchBox;
+ _entries[kItemTelegram].cursor = kCursorTelegram;
+ _entries[kItemPassengerList].cursor = kCursorPassengerList;
+ _entries[kItemArticle].cursor = kCursorArticle;
+ _entries[kItemScarf].cursor = kCursorScarf;
+ _entries[kItemPaper].cursor = kCursorPaper;
+ _entries[kItemParchemin].cursor = kCursorParchemin;
+ _entries[kItemMatch].cursor = kCursorMatch;
+ _entries[kItemWhistle].cursor = kCursorWhistle;
+ _entries[kItemKey].cursor = kCursorKey;
+ _entries[kItemBomb].cursor = kCursorBomb;
+ _entries[kItemFirebird].cursor = kCursorFirebird;
+ _entries[kItemBriefcase].cursor = kCursorBriefcase;
+ _entries[kItemCorpse].cursor = kCursorCorpse;
+
+ // Selectable
+ _entries[kItemMatchBox].isSelectable = true;
+ _entries[kItemMatch].isSelectable = true;
+ _entries[kItemTelegram].isSelectable = true;
+ _entries[kItemWhistle].isSelectable = true;
+ _entries[kItemKey].isSelectable = true;
+ _entries[kItemFirebird].isSelectable = true;
+ _entries[kItemBriefcase].isSelectable = true;
+ _entries[kItemCorpse].isSelectable = true;
+ _entries[kItemPassengerList].isSelectable = true;
+
+ // Auto selection
+ _entries[kItem2].manualSelect = false;
+ _entries[kItem3].manualSelect = false;
+ _entries[kItem5].manualSelect = false;
+ _entries[kItem7].manualSelect = false;
+ _entries[kItem9].manualSelect = false;
+ _entries[kItem11].manualSelect = false;
+ _entries[kItemBeetle].manualSelect = false;
+ _entries[kItem17].manualSelect = false;
+ _entries[kItemFirebird].manualSelect = false;
+ _entries[kItemBriefcase].manualSelect = false;
+ _entries[kItemCorpse].manualSelect = false;
+ _entries[kItemGreenJacket].manualSelect = false;
+ _entries[kItem22].manualSelect = false;
+
+ // Scene
+ _entries[kItemMatchBox].scene = kSceneMatchbox;
+ _entries[kItemTelegram].scene = kSceneTelegram;
+ _entries[kItemPassengerList].scene = kScenePassengerList;
+ _entries[kItemScarf].scene = kSceneScarf;
+ _entries[kItemParchemin].scene = kSceneParchemin;
+ _entries[kItemArticle].scene = kSceneArticle;
+ _entries[kItemPaper].scene = kScenePaper;
+ _entries[kItemFirebird].scene = kSceneFirebird;
+ _entries[kItemBriefcase].scene = kSceneBriefcase;
+
+ // Has item
+ _entries[kItemTelegram].isPresent = true;
+ _entries[kItemArticle].isPresent = true;
+
+ _selectedItem = kItemNone;
+}
+
+// FIXME we need to draw cursors with full background opacity so that whatever is in the background is erased
+// this saved us clearing some part of the background when switching between states
+
+// TODO if we draw inventory objects on screen, we need to load a new scene.
+// Signal that the inventory has taken over the screen and stop processing mouse events after we have been called
+bool Inventory::handleMouseEvent(const Common::Event &ev) {
+
+ // Do not show inventory when on the menu screen
+ if (getMenu()->isShown() || !_visible)
+ return false;
+
+ // Flag to know whether to restore the current cursor or not
+ bool insideInventory = false;
+
+ // Egg (menu)
+ if (_menuRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show the menu
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ getSound()->playSound(kEntityPlayer, "LIB039");
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+
+ // TODO can we return directly or do we need to make sure the state will be "valid" when we come back from the menu
+ return true;
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != getMenu()->getGameId() + 39) {
+ _highlightedItem = (InventoryItem)(getMenu()->getGameId() + 39);
+ drawItem(608, 448, _highlightedItem, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == getMenu()->getGameId() + 39) {
+ drawItem(608, 448, _highlightedItem, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // Portrait (inventory)
+ if (_inventoryRect.contains(ev.mouse)) {
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ // If clicked, show pressed state and display inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ open();
+ } else {
+ // Highlight if needed
+ if (_highlightedItem != (InventoryItem)getProgress().portrait && !_opened) {
+ _highlightedItem = (InventoryItem)getProgress().portrait;
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ askForRedraw();
+ }
+ }
+ } else {
+ // remove highlight if needed
+ if (_highlightedItem == (InventoryItem)getProgress().portrait && !_opened) {
+ drawItem(0, 0, getProgress().portrait, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ // If the inventory is open, check all items rect to see if we need to highlight one / handle click
+ if (_opened) {
+
+ // Always show normal cursor when the inventory is opened
+ insideInventory = true;
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ bool selected = false;
+
+ // Iterate over items
+ int16 y = 44;
+ for (int i = 1; i < 32; i++) {
+ if (!hasItem((InventoryItem)i))
+ continue;
+
+ if (Common::Rect(0, y, 32, 32 + y).contains(ev.mouse)) {
+
+ // If released with an item highlighted, show this item
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ if (_entries[i].isSelectable) {
+ selected = true;
+ _selectedItem = (InventoryItem)i;
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ }
+
+ examine((InventoryItem)i);
+ break;
+ } else {
+ if (_highlightedItem != i) {
+ drawItem(0, y, _entries[i].cursor, 100)
+ _highlightedItem = (InventoryItem)i;
+ askForRedraw();
+ }
+ }
+ } else {
+ // Remove highlight if necessary
+ if (_highlightedItem == i) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ _highlightedItem = kItemNone;
+ askForRedraw();
+ }
+ }
+
+ y += 40;
+ }
+
+ // Right button is released: we need to close the inventory
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ // Not on a selectable item: unselect the current item
+ if (!selected)
+ unselectItem();
+
+ close();
+ }
+ }
+
+ // Selected item
+ if (_selectedItem != kItemNone && _selectedRect.contains(ev.mouse)) {
+ insideInventory = true;
+
+ // Show magnifier icon
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ examine((InventoryItem)_selectedItem);
+ }
+ }
+
+ // If the egg is blinking, refresh
+ if (_blinkingEgg)
+ drawEgg();
+
+ // Restore cursor
+ //if (!insideInventory)
+ // _engine->getCursor()->setStyle(getLogic()->getCursorStyle());
+
+ return insideInventory;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// UI
+//////////////////////////////////////////////////////////////////////////
+void Inventory::show() {
+ clearBg(GraphicsManager::kBackgroundInventory);
+ askForRedraw();
+
+ // Show portrait (first draw, cannot be highlighted)
+ drawItem(0, 0, getProgress().portrait, 50)
+
+ // Show selected item
+ if (_selectedItem != kItemNone)
+ drawItem(44, 0, _selectedItem, 100)
+
+ drawEgg();
+}
+
+void Inventory::setPortrait(InventoryItem item) const {
+ getProgress().portrait = item;
+ drawItem(0, 0, getProgress().portrait, 50);
+}
+
+void Inventory::blinkEgg(bool enabled) {
+ _blinkingEgg = enabled;
+
+ // Reset state
+ _showingHourGlass = false;
+
+ // Show egg at full brightness for first step if blinking
+ if (_blinkingEgg)
+ drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+ else {
+ // Reset values
+ _blinkingBrightness = 100;
+ _blinkingInterval = _defaultBlinkingInterval;
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50) // normal egg state
+ }
+
+ askForRedraw();
+}
+
+void Inventory::showHourGlass() const{
+ if (!getFlags()->flag_5) {
+ drawItem(608, 448, kCursorHourGlass, 100);
+ }
+
+ askForRedraw();
+
+ getFlags()->shouldDrawEggOrHourGlass = true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Items
+//////////////////////////////////////////////////////////////////////////
+Inventory::InventoryEntry *Inventory::get(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ error("Inventory::getEntry: Invalid inventory item!");
+
+ return &_entries[item];
+}
+
+void Inventory::addItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = true;
+ get(item)->location = kObjectLocationNone;
+
+ // Auto-select item if necessary
+ if (get(item)->cursor && !get(item)->manualSelect) {
+ _selectedItem = (InventoryItem)get(item)->cursor;
+ drawItem(44, 0, _selectedItem, 100)
+ askForRedraw();
+ }
+}
+
+void Inventory::removeItem(InventoryItem item, ObjectLocation newLocation) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ get(item)->isPresent = false;
+ get(item)->location = newLocation;
+
+ if (get(item)->cursor == (CursorStyle)_selectedItem) {
+ _selectedItem = kItemNone;
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+ }
+}
+
+bool Inventory::hasItem(InventoryItem item) {
+ if (get(item)->isPresent && item < kPortraitOriginal)
+ return true;
+
+ return false;
+}
+
+void Inventory::selectItem(InventoryItem item) {
+ _selectedItem = item;
+
+ drawItem(44, 0, get(_selectedItem)->cursor, 100)
+ askForRedraw();
+}
+
+void Inventory::unselectItem() {
+ _selectedItem = kItemNone;
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32));
+ askForRedraw();
+}
+
+void Inventory::setLocationAndProcess(InventoryItem item, ObjectLocation location) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ if (get(item)->location == location)
+ return;
+
+ get(item)->location = location;
+
+ if (isItemSceneParameter(item) && !getFlags()->flag_0)
+ getScenes()->processScene();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Inventory::saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(_entries); i++)
+ _entries[i].saveLoadWithSerializer(s);
+}
+
+void Inventory::saveSelectedItem(Common::Serializer &s) {
+ s.syncAsUint32LE(_selectedItem);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Inventory::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < kPortraitOriginal; i++)
+ ret += Common::String::printf("%d : %s\n", i, _entries[i].toString().c_str());
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+InventoryItem Inventory::getFirstExaminableItem() const {
+
+ int index = 0;
+ InventoryEntry entry = _entries[index];
+ while (!entry.isPresent || !entry.cursor || entry.manualSelect) {
+ index++;
+ entry = _entries[index];
+
+ if (index >= kPortraitOriginal)
+ return kItemNone;
+ }
+
+ return (InventoryItem)index;
+}
+
+bool Inventory::isItemSceneParameter(InventoryItem item) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch(scene->type) {
+ default:
+ return false;
+
+ case Scene::kTypeItem:
+ if (scene->param1 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem2:
+ if (scene->param1 == item || scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeObjectItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+
+ case Scene::kTypeItem3:
+ if (scene->param1 == item || scene->param2 == item || scene->param3 == item)
+ return true;
+ break;
+
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param2 == item)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+// Examine an inventory item
+void Inventory::examine(InventoryItem item) {
+ SceneIndex index = get(item)->scene;
+ if (!index)
+ return;
+
+ /*if (!getState()->sceneUseBackup ||
+ (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem))
+ flag = 1;*/
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneBackup = getState()->scene;
+ getState()->sceneUseBackup = true;
+
+ getScenes()->loadScene(index);
+ } else {
+
+ if (!getState()->sceneBackup2)
+ return;
+
+ if (getFirstExaminableItem() == _selectedItem) {
+ index = getState()->sceneBackup2;
+ getState()->sceneBackup2 = kSceneNone;
+ getScenes()->loadScene(index);
+ }
+ }
+}
+
+// FIXME: see different callers and adjust
+// - draw with different brightness if mousing over
+void Inventory::drawEgg() const {
+ if (!getFlags()->flag_5)
+ drawItem(608, 448, getMenu()->getGameId() + 39, 50)
+
+ getFlags()->shouldDrawEggOrHourGlass = false;
+}
+
+// Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit.
+void Inventory::drawBlinkingEgg() {
+
+ warning("Inventory::drawEgg - blinking not implemented!");
+
+ //// TODO show egg (with or without mouseover)
+
+ //// Play timer sound
+ //if (getGlobalTimer() < 90) {
+ // if (getGlobalTimer() + ticks >= 90)
+ // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer);
+
+ // if (getSound()->isBuffered("TIMER"))
+ // setGlobalTimer(0);
+ //}
+
+ //// Restore egg to standard brightness
+ //if (!getGlobalTimer()) {
+ //
+ //}
+
+
+ //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness)
+
+ //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time
+ //
+
+ //// Reset values and stop blinking
+ //if (_blinkingTime == 0)
+ // blinkEgg(false);
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::open() {
+ _opened = true;
+
+ // Show selected state
+ drawItem(0, 0, getProgress().portrait + 1, 100)
+
+ int16 y = 44;
+
+ // Iterate over items
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ drawItem(0, y, _entries[i].cursor, 50)
+ y += 40;
+ }
+ }
+
+ askForRedraw();
+}
+
+// Close inventory: clear items and reset icon
+void Inventory::close() {
+ _opened = false;
+
+ // Fallback to unselected state
+ drawItem(0, 0, getProgress().portrait, 100)
+
+ // Erase rectangle for all inventory items
+ int count = 0;
+ for (uint i = 1; i < 32; i++) {
+ if (_entries[i].isPresent) {
+ count++;
+ }
+ }
+
+ _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(44 + 44 * count)));
+
+ askForRedraw();
+}
+
+Common::Rect Inventory::getItemRect(int16 index) const{
+ return Common::Rect(0, (int16)((32 + 12) * (index + 1)), 32, (int16)((32 + 12) * (index + 2))); // space between items = 12px
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h
new file mode 100644
index 0000000000..af3478bfae
--- /dev/null
+++ b/engines/lastexpress/game/inventory.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_INVENTORY_H
+#define LASTEXPRESS_INVENTORY_H
+
+/*
+ Inventory entry (32 entries)
+ ----------------------------
+
+ byte {1} - Item ID (set to 0 for "undefined" items)
+ byte {1} - Scene ID
+ byte {1} - ??
+ byte {1} - Selectable (1 if item is selectable, 0 otherwise)
+ byte {1} - Is item in inventory (set to 1 for telegram and article)
+ byte {1} - Auto selection (1 for no auto selection, 0 otherwise)
+ byte {1} - Location
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+
+class Inventory : Common::Serializable, public EventHandler {
+public:
+
+ // Entry
+ struct InventoryEntry : Common::Serializable {
+ CursorStyle cursor;
+ SceneIndex scene;
+ byte field_2;
+ bool isSelectable;
+ bool isPresent;
+ bool manualSelect;
+ ObjectLocation location;
+
+ InventoryEntry() {
+ cursor = kCursorNormal;
+ scene = kSceneNone;
+ field_2 = 0;
+ isSelectable = false;
+ isPresent = false;
+ manualSelect = true;
+ location = kObjectLocationNone;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("{ %d - %d - %d - %d - %d - %d - %d }", cursor, scene, field_2, isSelectable, isPresent, manualSelect, location);
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsByte(cursor);
+ s.syncAsByte(scene);
+ s.syncAsByte(field_2);
+ s.syncAsByte(isSelectable);
+ s.syncAsByte(isPresent);
+ s.syncAsByte(manualSelect);
+ s.syncAsByte(location);
+ }
+ };
+
+ Inventory(LastExpressEngine *engine);
+ ~Inventory();
+
+ // Inventory contents
+ void addItem(InventoryItem item);
+ void removeItem(InventoryItem item, ObjectLocation newLocation = kObjectLocationNone);
+ bool hasItem(InventoryItem item);
+ void selectItem(InventoryItem item);
+ void unselectItem();
+ InventoryItem getSelectedItem() { return _selectedItem; }
+
+ InventoryEntry *get(InventoryItem item);
+ InventoryEntry *getSelectedEntry() { return get(_selectedItem); }
+
+ InventoryItem getFirstExaminableItem() const;
+ void setLocationAndProcess(InventoryItem item, ObjectLocation location);
+
+ // UI Control
+ void show();
+ void blinkEgg(bool enabled);
+ void showHourGlass() const;
+ void setPortrait(InventoryItem item) const;
+ void drawEgg() const;
+ void drawBlinkingEgg();
+
+ // Handle inventory UI events.
+ bool handleMouseEvent(const Common::Event &ev);
+
+ // State
+ bool isMagnifierInUse() { return _flagUseMagnifier; }
+ bool isFlag1() { return _flag1; }
+ bool isFlag2() { return _flag2; }
+ bool isEggHighlighted() { return _flagEggHightlighted; }
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+ void saveSelectedItem(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms
+
+ LastExpressEngine *_engine;
+
+ InventoryEntry _entries[32];
+ InventoryItem _selectedItem;
+ InventoryItem _highlightedItem;
+ bool _opened;
+ bool _visible;
+
+ bool _showingHourGlass;
+ bool _blinkingEgg;
+ uint32 _blinkingTime;
+ uint32 _blinkingInterval;
+ uint32 _blinkingBrightness;
+
+ // Flags
+ bool _flagUseMagnifier;
+ bool _flag1;
+ bool _flag2;
+ bool _flagEggHightlighted;
+
+ Scene *_itemScene;
+
+ // Important rects
+ Common::Rect _inventoryRect;
+ Common::Rect _menuRect;
+ Common::Rect _selectedRect;
+
+ void init();
+
+ void open();
+ void close();
+ void examine(InventoryItem item);
+ Common::Rect getItemRect(int16 index) const;
+
+ bool isItemSceneParameter(InventoryItem item) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_INVENTORY_H
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp
new file mode 100644
index 0000000000..3ef710a324
--- /dev/null
+++ b/engines/lastexpress/game/logic.cpp
@@ -0,0 +1,593 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/logic.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+
+// Entities
+#include "lastexpress/entities/chapters.h"
+
+// Game
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+#define EVENT_TICKS_BEETWEEN_SAVEGAMES 450
+#define GAME_TICKS_BEETWEEN_SAVEGAMES 2700
+
+Logic::Logic(LastExpressEngine *engine) : _engine(engine) {
+ _action = new Action(engine);
+ _beetle = new Beetle(engine);
+ _entities = new Entities(engine);
+ _fight = new Fight(engine);
+ _saveload = new SaveLoad(engine);
+ _state = new State(engine);
+
+ // Flags
+ _flagActionPerformed = false;
+ _ignoreFrameInterval = false;
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+}
+
+Logic::~Logic() {
+ delete _action;
+ delete _beetle;
+ delete _fight;
+ delete _entities;
+ delete _saveload;
+ delete _state;
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Event Handling
+//////////////////////////////////////////////////////////////////////////
+#define REDRAW_CURSOR() { \
+ if (getInventory()->isMagnifierInUse()) \
+ _engine->getCursor()->setStyle(kCursorMagnifier); \
+ if (getInventory()->isFlag1() \
+ || getInventory()->isFlag2() \
+ || getInventory()->isEggHighlighted()) \
+ _engine->getCursor()->setStyle(kCursorNormal); \
+ return; \
+}
+
+void Logic::eventMouse(const Common::Event &ev) {
+ bool hotspotHandled = false;
+
+ // Reset mouse flags
+ getFlags()->mouseLeftClick = false;
+ getFlags()->mouseRightClick = false;
+
+ // Process event flags
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ if (getFlags()->frameInterval)
+ _ignoreFrameInterval = false;
+
+ getFlags()->frameInterval = false;
+ }
+
+ if (getFlags()->flag_0) {
+ if (ev.type == Common::EVENT_LBUTTONUP || ev.type == Common::EVENT_RBUTTONUP) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ getFlags()->frameInterval = true;
+ }
+ return;
+ }
+
+ if (_ignoreFrameInterval && getScenes()->checkCurrentPosition(true) && _engine->getCursor()->getStyle() == kCursorForward) {
+ getFlags()->shouldRedraw = false;
+ getFlags()->flag_0 = true;
+ return;
+ }
+
+ // Update coordinates
+ getGameState()->setCoordinates(ev.mouse);
+
+ // Handle inventory
+ getInventory()->handleMouseEvent(ev);
+
+ // Stop processing is inside the menu
+ if (getMenu()->isShown())
+ return;
+
+ // Handle whistle case
+ if (getInventory()->getSelectedItem() == kItemWhistle
+ && !getProgress().isEggOpen
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemWhistle)->cursor);
+
+ // Check if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP && !getSound()->isBuffered("LIB045")) {
+
+ getSound()->playSoundEvent(kEntityPlayer, 45);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 26) || getEntities()->isPlayerPosition(kCarGreenSleeping, 25) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction226078300);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 26) || getEntities()->isPlayerPosition(kCarRedSleeping, 25) || getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction226078300);
+ }
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle match case
+ if (getInventory()->getSelectedItem() == kItemMatch
+ && (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && getProgress().jacket == kJacketGreen
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()
+ && (getInventory()->get(kItem2)->location == kObjectLocationNone || getEntityData(kEntityPlayer)->car != kCarRedSleeping || getEntityData(kEntityPlayer)->entityPosition != kPosition_2300)) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemMatch)->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+
+ getScenes()->processScene();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle entity item case
+ EntityIndex entityIndex = getEntities()->canInteractWith(ev.mouse);
+ if (entityIndex
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ InventoryItem item = getEntityData(entityIndex)->inventoryItem;
+ if (getInventory()->hasItem((InventoryItem)(item & kItemToggleHigh))) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(item & kItemToggleHigh))->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, (InventoryItem)(item & kItemToggleHigh));
+ } else if ((InventoryItem)(item & kItemInvalid)) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(kCursorTalk2);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, kCursorNormal);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Handle standard actions
+ if (hotspotHandled || getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ return;
+
+ // Magnifier in use
+ if (getInventory()->isMagnifierInUse()) {
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted())
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ return;
+ }
+
+ // Check hotspots
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (!(*it)->isInside(ev.mouse))
+ continue;
+
+ if ((*it)->location < location)
+ continue;
+
+ if (!getAction()->getCursor(**it))
+ continue;
+
+ Scene *hotspotScene = getScenes()->get((*it)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*it)->cursor == kCursorTurnRight
+ || (*it)->cursor == kCursorTurnLeft) {
+ location = (*it)->location;
+ hotspot = *it;
+ }
+ }
+
+ // No hotspot found: show the normal cursor
+ if (!hotspot) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ return;
+ }
+
+ // Found an hotspot: update the cursor and perform the action if the user clicked the mouse
+ _engine->getCursor()->setStyle(getAction()->getCursor(*hotspot));
+
+ if (ev.type != Common::EVENT_LBUTTONUP || _flagActionPerformed)
+ return;
+
+ _flagActionPerformed = true;
+
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getFlags()->shouldRedraw = false;
+
+ getScenes()->setScene(testScene);
+
+ if (getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ // Switch to next chapter if necessary
+ if (hotspot->action == SceneHotspot::kActionSwitchChapter && hotspot->param1 == getState()->progress.chapter)
+ switchChapter();
+}
+
+void Logic::eventTick(const Common::Event &) {
+ uint ticks = 1;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust ticks if an action has been performed
+ if (_flagActionPerformed)
+ ticks = 10;
+
+ _flagActionPerformed = false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw the blinking egg if needed
+ if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawBlinkingEgg();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust time and save game if needed
+ if (getFlags()->isGameRunning) {
+ getState()->timeTicks += ticks;
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)(ticks * getState()->timeDelta));
+
+ if (getState()->timeDelta) {
+
+ // Auto-save
+ if (!_ticksSinceLastSavegame) {
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+ getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, kEventNone);
+ }
+
+ // Save after game ticks interval
+ if ((getState()->timeTicks - getSaveLoad()->getLastSavegameTicks()) > GAME_TICKS_BEETWEEN_SAVEGAMES)
+ getSaveLoad()->saveGame(kSavegameTypeTickInterval, kEntityChapters, kEventNone);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load scene and process hotspot
+ if (getFlags()->flag_0 && !getFlags()->mouseLeftClick && !getFlags()->mouseRightClick) {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (getScenes()->checkCurrentPosition(true)
+ && !getEntities()->getPosition(scene->car, scene->position)) {
+
+ // Process hotspot
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getScenes()->setScene(testScene);
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ if (getFlags()->isGameRunning)
+ getSavePoints()->callAndProcess();
+
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ return;
+ }
+
+ // Stop processing if the game is paused
+ if (!getFlags()->isGameRunning)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update beetle, savepoints, entities and draw frames
+ if (_beetle->isLoaded())
+ _beetle->update();
+
+ getSavePoints()->callAndProcess();
+ getEntities()->updateCallbacks();
+ getScenes()->drawFrames(true);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update cursor if we can interact with an entity
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (!entity) {
+ if (_engine->getCursor()->getStyle() >= kCursorTalk2)
+ updateCursor(false);
+
+ return;
+ }
+
+ // Show item cursor on entity
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh)) && (int)getEntityData(entity)->inventoryItem != (int)kCursorTalk2) {
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor);
+ return;
+ }
+
+ getLogic()->updateCursor(false);
+ _engine->getCursor()->setStyle(kCursorTalk2);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game over, Chapters & credits
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Resets the game state.
+ */
+void Logic::resetState() {
+ getState()->scene = kSceneDefault;
+
+ warning("Logic::resetState: not implemented! You need to restart the engine until this is implemented.");
+}
+
+/**
+ * Handle game over
+ *
+ * @param type The savegame type.
+ * @param value The value (event, time, index, ...)
+ * @param sceneIndex Index of the scene to show.
+ * @param showScene true to show a scene, false to return to menu directly
+ */
+void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const {
+
+ getSound()->processEntries();
+ getEntities()->reset();
+ getFlags()->isGameRunning = false;
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ if (showScene) {
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ if (sceneIndex && !getFlags()->mouseRightClick) {
+ getScenes()->loadScene(sceneIndex);
+
+ while (getSound()->isBuffered(kEntityTables4)) {
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+
+ // Show Menu
+ getMenu()->show(false, type, value);
+}
+
+void Logic::switchChapter() const {
+ getSound()->clearStatus();
+
+ switch(getState()->progress.chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->addItem(kItemMatchBox);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter2);
+ break;
+
+ case kChapter2:
+ getInventory()->addItem(kItemScarf);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter3);
+ break;
+
+ case kChapter3:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation4;
+ getInventory()->get(kItemFirebird)->isPresent = false;
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->addItem(kItemKey);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter4);
+ break;
+
+ case kChapter4:
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter5);
+ break;
+
+ case kChapter5:
+ playFinalSequence();
+ break;
+ }
+}
+
+void Logic::playFinalSequence() const {
+ getSound()->processEntries();
+
+ _action->playAnimation(kEventFinalSequence);
+ showCredits();
+
+ getEntities()->reset();
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+}
+
+void Logic::showCredits() const {
+ error("Logic::showCredits: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void Logic::updateCursor(bool) const { /* the cursor is always updated, even when we don't want to redraw it */
+ CursorStyle style = kCursorNormal;
+ bool interact = false;
+
+ if (getInventory()->getSelectedItem() != kItemWhistle
+ || getProgress().isEggOpen
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()) {
+
+ if (getInventory()->getSelectedItem() != kItemMatch
+ || (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getProgress().jacket != kJacketGreen
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()
+ || (getInventory()->get(kItem2)->location
+ && getEntityData(kEntityPlayer)->car == kCarRedSleeping
+ && getEntityData(kEntityPlayer)->entityPosition == kPosition_2300)) {
+
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (entity
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))) {
+ interact = true;
+ style = getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor;
+ } else if ((int)getEntityData(entity)->inventoryItem == kItemInvalid) {
+ interact = true;
+ style = kCursorTalk2;
+ }
+ }
+
+ if (!interact
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ // Check all hotspots
+ for (Common::Array<SceneHotspot *>::iterator i = scene->getHotspots()->begin(); i != scene->getHotspots()->end(); ++i) {
+ if ((*i)->isInside(getCoords()) && (*i)->location >= location) {
+ if (getAction()->getCursor(**i)) {
+ Scene *hotspotScene = getScenes()->get((*i)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*i)->cursor == kCursorTurnRight
+ || (*i)->cursor == kCursorTurnLeft) {
+ hotspot = *i;
+ location = (*i)->location;
+ }
+ }
+ }
+ }
+
+ style = (hotspot) ? getAction()->getCursor(*hotspot) : kCursorNormal;
+ }
+ } else {
+ style = getInventory()->get(kItemMatch)->cursor;
+ }
+
+ } else {
+ style = getInventory()->get(kItemWhistle)->cursor;
+ }
+
+ if (getInventory()->isMagnifierInUse())
+ style = kCursorMagnifier;
+
+ if (getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ style = kCursorNormal;
+
+ _engine->getCursor()->setStyle(style);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h
new file mode 100644
index 0000000000..fc867d7680
--- /dev/null
+++ b/engines/lastexpress/game/logic.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_LOGIC_H
+#define LASTEXPRESS_LOGIC_H
+
+#include "lastexpress/shared.h"
+
+#include "lastexpress/game/entities.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "common/events.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Action;
+class Beetle;
+class Debugger;
+class Entities;
+class Fight;
+class SaveLoad;
+class State;
+
+class Logic : public EventHandler {
+public:
+ Logic(LastExpressEngine *engine);
+ ~Logic();
+
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ void resetState();
+ void gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const;
+ void playFinalSequence() const;
+ void updateCursor(bool redraw = true) const;
+
+ Action *getGameAction() { return _action; }
+ Beetle *getGameBeetle() { return _beetle; }
+ Entities *getGameEntities() { return _entities; }
+ Fight *getGameFight() { return _fight; }
+ SaveLoad *getGameSaveLoad() { return _saveload; }
+ State *getGameState() { return _state; }
+
+private:
+ LastExpressEngine *_engine;
+
+ Action *_action; ///< Actions
+ Beetle *_beetle; ///< Beetle catching
+ Entities *_entities; ///< Entities
+ Fight *_fight; ///< Fight handling
+ SaveLoad *_saveload; ///< Save & loading
+ State *_state; ///< Game state
+
+ void switchChapter() const;
+ void showCredits() const;
+
+ // Flags & Members
+ bool _flagActionPerformed;
+ bool _ignoreFrameInterval;
+ int _ticksSinceLastSavegame;
+
+ friend class Debugger;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_LOGIC_H
diff --git a/engines/lastexpress/game/menu.cpp b/engines/lastexpress/game/menu.cpp
new file mode 100644
index 0000000000..f6840c28a7
--- /dev/null
+++ b/engines/lastexpress/game/menu.cpp
@@ -0,0 +1,1538 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/menu.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+#include "common/rational.h"
+
+#define getNextGameId() (GameId)((_gameId + 1) % 6)
+
+namespace LastExpress {
+
+// Bottom-left buttons (quit.seq)
+enum StartMenuButtons {
+ kButtonVolumeDownPushed,
+ kButtonVolumeDown,
+ kButtonVolume,
+ kButtonVolumeUp,
+ kButtonVolumeUpPushed,
+ kButtonBrightnessDownPushed, // 5
+ kButtonBrightnessDown,
+ kButtonBrightness,
+ kButtonBrightnessUp,
+ kButtonBrightnessUpPushed,
+ kButtonQuit, // 10
+ kButtonQuitPushed
+};
+
+// Egg buttons (buttns.seq)
+enum StartMenuEggButtons {
+ kButtonShield,
+ kButtonRewind,
+ kButtonRewindPushed,
+ kButtonForward,
+ kButtonForwardPushed,
+ kButtonCredits, // 5
+ kButtonCreditsPushed,
+ kButtonContinue
+};
+
+// Tooltips sequence (helpnewr.seq)
+enum StartMenuTooltips {
+ kTooltipInsertCd1,
+ kTooltipInsertCd2,
+ kTooltipInsertCd3,
+ kTooltipContinueGame,
+ kTooltipReplayGame,
+ kTooltipContinueRewoundGame, // 5
+ kTooltipViewGameEnding,
+ kTooltipStartAnotherGame,
+ kTooltipVolumeUp,
+ kTooltipVolumeDown,
+ kTooltipBrightnessUp, // 10
+ kTooltipBrightnessDown,
+ kTooltipQuit,
+ kTooltipRewindParis,
+ kTooltipForwardStrasbourg,
+ kTooltipRewindStrasbourg, // 15
+ kTooltipRewindMunich,
+ kTooltipForwardMunich,
+ kTooltipForwardVienna,
+ kTooltipRewindVienna,
+ kTooltipRewindBudapest, // 20
+ kTooltipForwardBudapest,
+ kTooltipForwardBelgrade,
+ kTooltipRewindBelgrade,
+ kTooltipForwardConstantinople,
+ kTooltipSwitchBlueGame, // 25
+ kTooltipSwitchRedGame,
+ kTooltipSwitchGoldGame,
+ kTooltipSwitchGreenGame,
+ kTooltipSwitchTealGame,
+ kTooltipSwitchPurpleGame, // 30
+ kTooltipPlayNewGame,
+ kTooltipCredits,
+ kTooltipFastForward,
+ kTooltipRewind
+};
+
+//////////////////////////////////////////////////////////////////////////
+// DATA
+//////////////////////////////////////////////////////////////////////////
+
+// Information about the cities on the train line
+static const struct {
+ uint8 frame;
+ TimeValue time;
+} _trainCities[31] = {
+ {0, kTimeCityParis},
+ {9, kTimeCityEpernay},
+ {11, kTimeCityChalons},
+ {16, kTimeCityBarLeDuc},
+ {21, kTimeCityNancy},
+ {25, kTimeCityLuneville},
+ {35, kTimeCityAvricourt},
+ {37, kTimeCityDeutschAvricourt},
+ {40, kTimeCityStrasbourg},
+ {53, kTimeCityBadenOos},
+ {56, kTimeCityKarlsruhe},
+ {60, kTimeCityStuttgart},
+ {63, kTimeCityGeislingen},
+ {66, kTimeCityUlm},
+ {68, kTimeCityAugsburg},
+ {73, kTimeCityMunich},
+ {84, kTimeCitySalzbourg},
+ {89, kTimeCityAttnangPuchheim},
+ {97, kTimeCityWels},
+ {100, kTimeCityLinz},
+ {104, kTimeCityAmstetten},
+ {111, kTimeCityVienna},
+ {120, kTimeCityPoszony},
+ {124, kTimeCityGalanta},
+ {132, kTimeCityBudapest},
+ {148, kTimeCityBelgrade},
+ /* Line 1 ends at 150 - line 2 begins at 0 */
+ {157, kTimeCityNish},
+ {165, kTimeCityTzaribrod},
+ {174, kTimeCitySofia},
+ {198, kTimeCityAdrianople},
+ {210, kTimeCityConstantinople}};
+
+static const struct {
+ TimeValue time;
+ uint index;
+ StartMenuTooltips rewind;
+ StartMenuTooltips forward;
+} _cityButtonsInfo[7] = {
+ {kTimeCityParis, 64, kTooltipRewindParis, kTooltipRewindParis},
+ {kTimeCityStrasbourg, 128, kTooltipRewindStrasbourg, kTooltipForwardStrasbourg},
+ {kTimeCityMunich, 129, kTooltipRewindMunich, kTooltipForwardMunich},
+ {kTimeCityVienna, 130, kTooltipRewindVienna, kTooltipForwardVienna},
+ {kTimeCityBudapest, 131, kTooltipRewindBudapest, kTooltipForwardBudapest},
+ {kTimeCityBelgrade, 132, kTooltipRewindBelgrade, kTooltipForwardBelgrade},
+ {kTimeCityConstantinople, 192, kTooltipForwardConstantinople, kTooltipForwardConstantinople}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clock
+//////////////////////////////////////////////////////////////////////////
+class Clock {
+public:
+ Clock(LastExpressEngine *engine);
+ ~Clock();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameMinutes;
+ SequenceFrame *_frameHour;
+ SequenceFrame *_frameSun;
+ SequenceFrame *_frameDate;
+};
+
+Clock::Clock(LastExpressEngine *engine) : _engine(engine), _frameMinutes(NULL), _frameHour(NULL), _frameSun(NULL), _frameDate(NULL) {
+ _frameMinutes = new SequenceFrame(loadSequence("eggmin.seq"), 0, true);
+ _frameHour = new SequenceFrame(loadSequence("egghour.seq"), 0, true);
+ _frameSun = new SequenceFrame(loadSequence("sun.seq"), 0, true);
+ _frameDate = new SequenceFrame(loadSequence("datenew.seq"), 0, true);
+}
+
+Clock::~Clock() {
+ delete _frameMinutes;
+ delete _frameHour;
+ delete _frameSun;
+ delete _frameDate;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void Clock::clear() {
+ getScenes()->removeFromQueue(_frameMinutes);
+ getScenes()->removeFromQueue(_frameHour);
+ getScenes()->removeFromQueue(_frameSun);
+ getScenes()->removeFromQueue(_frameDate);
+}
+
+void Clock::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameMinutes || !_frameHour || !_frameSun || !_frameDate)
+ error("Clock::process: clock sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Game starts at: 1037700 = 7:13 p.m. on July 24, 1914
+ // Game ends at: 4941000 = 7:30 p.m. on July 26, 1914
+ // Game lasts for: 3903300 = 2 days + 17 mins = 2897 mins
+
+ // 15 = 1 second
+ // 15 * 60 = 900 = 1 minute
+ // 900 * 60 = 54000 = 1 hour
+ // 54000 * 24 = 1296000 = 1 day
+
+ // Calculate each sequence index from the current time
+
+ uint8 hour = 0;
+ uint8 minute = 0;
+ State::getHourMinutes(time, &hour, &minute);
+ uint32 index_date = 18 * time / 1296000;
+ if (hour == 23)
+ index_date += 18 * minute / 60;
+
+ // Set sequences frames
+ _frameMinutes->setFrame(minute);
+ _frameHour->setFrame((5 * hour + minute / 12) % 60);
+ _frameSun->setFrame((5 * hour + minute / 12) % 120);
+ _frameDate->setFrame((uint16)index_date);
+
+ // Adjust z-order and queue
+ _frameMinutes->getInfo()->location = 1;
+ _frameHour->getInfo()->location = 1;
+ _frameSun->getInfo()->location = 1;
+ _frameDate->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameMinutes);
+ getScenes()->addToQueue(_frameHour);
+ getScenes()->addToQueue(_frameSun);
+ getScenes()->addToQueue(_frameDate);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// TrainLine
+//////////////////////////////////////////////////////////////////////////
+class TrainLine {
+public:
+ TrainLine(LastExpressEngine *engine);
+ ~TrainLine();
+
+ void draw(uint32 time);
+ void clear();
+
+private:
+ LastExpressEngine *_engine;
+
+ // Frames
+ SequenceFrame *_frameLine1;
+ SequenceFrame *_frameLine2;
+};
+
+TrainLine::TrainLine(LastExpressEngine *engine) : _engine(engine), _frameLine1(NULL), _frameLine2(NULL) {
+ _frameLine1 = new SequenceFrame(loadSequence("line1.seq"), 0, true);
+ _frameLine2 = new SequenceFrame(loadSequence("line2.seq"), 0, true);
+}
+
+TrainLine::~TrainLine() {
+ delete _frameLine1;
+ delete _frameLine2;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+void TrainLine::clear() {
+ getScenes()->removeFromQueue(_frameLine1);
+ getScenes()->removeFromQueue(_frameLine2);
+}
+
+// Draw the train line at the time
+// line1: 150 frames (=> Belgrade)
+// line2: 61 frames (=> Constantinople)
+void TrainLine::draw(uint32 time) {
+ assert(time >= kTimeCityParis && time <= kTimeCityConstantinople);
+
+ // Check that sequences have been loaded
+ if (!_frameLine1 || !_frameLine2)
+ error("TrainLine::process: Line sequences have not been loaded correctly!");
+
+ // Clear existing frames
+ clear();
+
+ // Get the index of the last city the train has visited
+ uint index = 0;
+ for (uint i = 0; i < ARRAYSIZE(_trainCities); i++)
+ if ((uint32)_trainCities[i].time <= time)
+ index = i;
+
+ uint16 frame;
+ if (time > (uint32)_trainCities[index].time) {
+ // Interpolate linearly to use a frame between the cities
+ uint8 diffFrames = _trainCities[index + 1].frame - _trainCities[index].frame;
+ uint diffTimeCities = (uint)(_trainCities[index + 1].time - _trainCities[index].time);
+ uint traveledTime = (time - (uint)_trainCities[index].time);
+ frame = (uint16)(_trainCities[index].frame + (traveledTime * diffFrames) / diffTimeCities);
+ } else {
+ // Exactly on the city
+ frame = _trainCities[index].frame;
+ }
+
+ // Set frame, z-order and queue
+ if (frame < 150) {
+ _frameLine1->setFrame(frame);
+
+ _frameLine1->getInfo()->location = 1;
+ getScenes()->addToQueue(_frameLine1);
+ } else {
+ // We passed Belgrade
+ _frameLine1->setFrame(149);
+ _frameLine2->setFrame(frame - 150);
+
+ _frameLine1->getInfo()->location = 1;
+ _frameLine2->getInfo()->location = 1;
+
+ getScenes()->addToQueue(_frameLine1);
+ getScenes()->addToQueue(_frameLine2);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Menu
+//////////////////////////////////////////////////////////////////////////
+Menu::Menu(LastExpressEngine *engine) : _engine(engine),
+ _seqTooltips(NULL), _seqEggButtons(NULL), _seqButtons(NULL), _seqAcorn(NULL), _seqCity1(NULL), _seqCity2(NULL), _seqCity3(NULL), _seqCredits(NULL),
+ _gameId(kGameBlue), _hasShownStartScreen(false), _hasShownIntro(false),
+ _isShowingCredits(false), _isGameStarted(false), _isShowingMenu(false),
+ _creditsSequenceIndex(0), _checkHotspotsTicks(15), _mouseFlags(Common::EVENT_INVALID), _lastHotspot(NULL),
+ _currentTime(kTimeNone), _lowerTime(kTimeNone), _time(kTimeNone), _currentIndex(0), _index(0), _lastIndex(0), _delta(0), _handleTimeDelta(false) {
+
+ _clock = new Clock(_engine);
+ _trainLine = new TrainLine(_engine);
+}
+
+Menu::~Menu() {
+ delete _clock;
+ delete _trainLine;
+
+ SAFE_DELETE(_seqTooltips);
+ SAFE_DELETE(_seqEggButtons);
+ SAFE_DELETE(_seqButtons);
+ SAFE_DELETE(_seqAcorn);
+ SAFE_DELETE(_seqCity1);
+ SAFE_DELETE(_seqCity2);
+ SAFE_DELETE(_seqCity3);
+ SAFE_DELETE(_seqCredits);
+
+ _lastHotspot = NULL;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Setup
+void Menu::setup() {
+
+ // Clear drawing queue
+ getScenes()->removeAndRedraw(&_frames[kOverlayAcorn], false);
+ SAFE_DELETE(_seqAcorn);
+
+ // Load Menu scene
+ // + 1 = normal menu with open egg / clock
+ // + 2 = shield menu, when no savegame exists (no game has been started)
+ _isGameStarted = _lowerTime >= kTimeStartGame;
+ getScenes()->loadScene((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2));
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load Acorn sequence
+ _seqAcorn = loadSequence(getAcornSequenceName(_isGameStarted ? getNextGameId() : kGameBlue));
+
+ //////////////////////////////////////////////////////////////////////////
+ // Check if we loaded sequences before
+ if (_seqTooltips && _seqTooltips->count() > 0)
+ return;
+
+ // Load all static data
+ _seqTooltips = loadSequence("helpnewr.seq");
+ _seqEggButtons = loadSequence("buttns.seq");
+ _seqButtons = loadSequence("quit.seq");
+ _seqCity1 = loadSequence("jlinetl.seq");
+ _seqCity2 = loadSequence("jlinecen.seq");
+ _seqCity3 = loadSequence("jlinebr.seq");
+ _seqCredits = loadSequence("credits.seq");
+
+ _frames[kOverlayTooltip] = new SequenceFrame(_seqTooltips);
+ _frames[kOverlayEggButtons] = new SequenceFrame(_seqEggButtons);
+ _frames[kOverlayButtons] = new SequenceFrame(_seqButtons);
+ _frames[kOverlayAcorn] = new SequenceFrame(_seqAcorn);
+ _frames[kOverlayCity1] = new SequenceFrame(_seqCity1);
+ _frames[kOverlayCity2] = new SequenceFrame(_seqCity2);
+ _frames[kOverlayCity3] = new SequenceFrame(_seqCity3);
+ _frames[kOverlayCredits] = new SequenceFrame(_seqCredits);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Handle events
+void Menu::eventMouse(const Common::Event &ev) {
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ bool redraw = true;
+ getFlags()->shouldRedraw = false;
+
+ // Update coordinates
+ setCoords(ev.mouse);
+ //_mouseFlags = (Common::EventType)(ev.type & Common::EVENT_LBUTTONUP);
+
+ if (_isShowingCredits) {
+ if (ev.type == Common::EVENT_RBUTTONUP) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ }
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+ // Last frame of the credits
+ if (_seqCredits && _creditsSequenceIndex == _seqCredits->count() - 1) {
+ showFrame(kOverlayCredits, -1, true);
+ _isShowingCredits = false;
+ } else {
+ ++_creditsSequenceIndex;
+ showFrame(kOverlayCredits, _creditsSequenceIndex, true);
+ }
+ }
+ } else {
+ // Check for hotspots
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(ev.mouse, &hotspot);
+
+ if (_lastHotspot != hotspot || ev.type == Common::EVENT_LBUTTONUP) {
+ _lastHotspot = hotspot;
+
+ if (ev.type == Common::EVENT_MOUSEMOVE) { /* todo check event type */
+ if (!_handleTimeDelta && hasTimeDelta())
+ setTime();
+ }
+
+ if (hotspot) {
+ redraw = handleEvent((StartMenuAction)hotspot->action, ev.type);
+ getFlags()->mouseRightClick = false;
+ getFlags()->mouseLeftClick = false;
+ } else {
+ hideOverlays();
+ }
+ }
+ }
+
+ if (redraw) {
+ getFlags()->shouldRedraw = true;
+ askForRedraw();
+ }
+}
+
+void Menu::eventTick(const Common::Event&) {
+ if (hasTimeDelta())
+ adjustTime();
+ else if (_handleTimeDelta)
+ _handleTimeDelta = false;
+
+ // Check hotspots
+ if (!--_checkHotspotsTicks) {
+ checkHotspots();
+ _checkHotspotsTicks = 15;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Show the intro and load the main menu scene
+void Menu::show(bool doSavegame, SavegameType type, uint32 value) {
+
+ if (_isShowingMenu)
+ return;
+
+ _isShowingMenu = true;
+ getEntities()->reset();
+
+ // If no blue savegame exists, this might be the first time we start the game, so we show the full intro
+ if (!getFlags()->mouseRightClick) {
+ if (!SaveLoad::isSavegameValid(kGameBlue) && _engine->getResourceManager()->loadArchive(kArchiveCd1)) {
+
+ if (!_hasShownIntro) {
+ // Show Broderbrund logo
+ Animation animation;
+ if (animation.load(getArchive("1930.nis")))
+ animation.play();
+
+ getFlags()->mouseRightClick = false;
+
+ // Play intro music
+ getSound()->playSoundWithSubtitles("MUS001.SND", SoundManager::kFlagMusic, kEntityPlayer);
+
+ // Show The Smoking Car logo
+ if (animation.load(getArchive("1931.nis")))
+ animation.play();
+
+ _hasShownIntro = true;
+ }
+ } else {
+ // Only show the quick intro
+ if (!_hasShownStartScreen) {
+ getSound()->playSoundWithSubtitles("MUS018.SND", SoundManager::kFlagMusic, kEntityPlayer);
+ getScenes()->loadScene(kSceneStartScreen);
+
+ // Original game waits 60 frames and loops Sound::unknownFunction1 unless the right button is pressed
+ uint32 nextFrameCount = getFrameCount() + 60;
+ while (getFrameCount() < nextFrameCount) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+ }
+
+ _hasShownStartScreen = true;
+
+ // Init Menu
+ init(doSavegame, type, value);
+
+ // Setup sound
+ getSound()->unknownFunction4();
+ getSound()->resetQueue(SoundManager::kSoundType11, SoundManager::kSoundType13);
+ if (getSound()->isBuffered("TIMER"))
+ getSound()->removeFromQueue("TIMER");
+
+ // Init flags & misc
+ _isShowingCredits = false;
+ _handleTimeDelta = hasTimeDelta();
+ getInventory()->unselectItem();
+
+ // Set Cursor type
+ _engine->getCursor()->setStyle(kCursorNormal);
+ _engine->getCursor()->show(true);
+
+ setup();
+ checkHotspots();
+
+ // Set event handlers
+ SET_EVENT_HANDLERS(Menu, this);
+}
+
+bool Menu::handleEvent(StartMenuAction action, Common::EventType type) {
+ bool clicked = (type == Common::EVENT_LBUTTONUP);
+
+ switch(action) {
+ default:
+ hideOverlays();
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCredits:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayEggButtons, kButtonCreditsPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ hideOverlays();
+
+ _isShowingCredits = true;
+ _creditsSequenceIndex = 0;
+
+ showFrame(kOverlayCredits, 0, true);
+ } else {
+ // TODO check flags ?
+
+ showFrame(kOverlayEggButtons, kButtonCredits, true);
+ showFrame(kOverlayTooltip, kTooltipCredits, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuQuitGame:
+ showFrame(kOverlayTooltip, kTooltipQuit, true);
+
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonQuitPushed, true);
+
+ getSound()->clearStatus();
+ getSound()->updateQueue();
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // FIXME uncomment when sound queue is properly implemented
+ /*while (getSound()->isBuffered("LIB046"))
+ getSound()->updateQueue();*/
+
+ getFlags()->shouldRedraw = false;
+
+ Engine::quitGame();
+
+ return false;
+ } else {
+ showFrame(kOverlayButtons, kButtonQuit, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuCase4:
+ if (clicked)
+ _index = 0;
+ // fall down to kMenuContinue
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuContinue: {
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Determine the proper CD archive
+ ArchiveIndex cd = kArchiveCd1;
+ if (getProgress().chapter > kChapter1)
+ cd = (getProgress().chapter > kChapter3) ? kArchiveCd3 : kArchiveCd2;
+
+ // Show tooltips & buttons to start a game, continue a game or load the proper cd
+ if (ResourceManager::isArchivePresent(cd)) {
+ if (_isGameStarted) {
+ showFrame(kOverlayEggButtons, kButtonContinue, true);
+
+ if (_lastIndex == _index) {
+ showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _lastIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true);
+ } else {
+ showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true);
+ }
+
+ } else {
+ showFrame(kOverlayEggButtons, kButtonShield, true);
+ showFrame(kOverlayTooltip, kTooltipPlayNewGame, true);
+ }
+ } else {
+ showFrame(kOverlayEggButtons, -1, true);
+ showFrame(kOverlayTooltip, cd - 1, true);
+ }
+
+ if (!clicked)
+ break;
+
+ // Try loading the archive file
+ if (!_engine->getResourceManager()->loadArchive(cd))
+ break;
+
+ // Load the train data file and setup game
+ getScenes()->loadSceneDataFile(cd);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ // Setup new game
+ getSavePoints()->reset();
+ setLogicEventHandlers();
+
+ if (_index) {
+ getSound()->processEntry(SoundManager::kSoundType11);
+ } else {
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 3));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 4));
+
+ if (!getFlags()->mouseRightClick) {
+ getScenes()->loadScene((SceneIndex)(5 * _gameId + 5));
+
+ if (!getFlags()->mouseRightClick) {
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ // Show intro
+ Animation animation;
+ if (animation.load(getArchive("1601.nis")))
+ animation.play();
+
+ getEvent(kEventIntro) = 1;
+ }
+ }
+ }
+ }
+
+ if (!getEvent(kEventIntro)) {
+ getEvent(kEventIntro) = 1;
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+ }
+ }
+
+ // Setup game
+ getFlags()->isGameRunning = true;
+ startGame();
+
+ if (!_isShowingMenu)
+ getInventory()->show();
+
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuSwitchSaveGame:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ showFrame(kOverlayAcorn, 1, true);
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB047");
+
+ // Setup new menu screen
+ switchGame();
+ setup();
+
+ // Set fight state to 0
+ getFight()->resetState();
+
+ return true;
+ }
+
+ // TODO Check for flag
+
+ showFrame(kOverlayAcorn, 0, true);
+
+ if (_isGameStarted) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (_gameId == kGameGold) {
+ showFrame(kOverlayTooltip, kTooltipSwitchBlueGame, true);
+ break;
+ }
+
+ if (!SaveLoad::isSavegameValid(getNextGameId())) {
+ showFrame(kOverlayTooltip, kTooltipStartAnotherGame, true);
+ break;
+ }
+
+ // Stupid tooltips ids are not in order, so we can't just increment them...
+ switch(_gameId) {
+ default:
+ break;
+
+ case kGameBlue:
+ showFrame(kOverlayTooltip, kTooltipSwitchRedGame, true);
+ break;
+
+ case kGameRed:
+ showFrame(kOverlayTooltip, kTooltipSwitchGreenGame, true);
+ break;
+
+ case kGameGreen:
+ showFrame(kOverlayTooltip, kTooltipSwitchPurpleGame, true);
+ break;
+
+ case kGamePurple:
+ showFrame(kOverlayTooltip, kTooltipSwitchTealGame, true);
+ break;
+
+ case kGameTeal:
+ showFrame(kOverlayTooltip, kTooltipSwitchGoldGame, true);
+ break;
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuRewindGame:
+ if (!_index || _currentTime < _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonRewindPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ rewindTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonRewind, true);
+ showFrame(kOverlayTooltip, kTooltipRewind, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuForwardGame:
+ if (_lastIndex <= _index || _currentTime > _time) {
+ hideOverlays();
+ break;
+ }
+
+ if (clicked) {
+ if (hasTimeDelta())
+ _handleTimeDelta = false;
+
+ showFrame(kOverlayEggButtons, kButtonForwardPushed, true);
+ showFrame(kOverlayTooltip, -1, true);
+
+ getSound()->playSound(kEntityPlayer, "LIB046");
+
+ forwardTime();
+
+ _handleTimeDelta = false;
+ } else {
+ showFrame(kOverlayEggButtons, kButtonForward, true);
+ showFrame(kOverlayTooltip, kTooltipFastForward, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuParis:
+ moveToCity(kParis, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuStrasBourg:
+ moveToCity(kStrasbourg, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuMunich:
+ moveToCity(kMunich, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuVienna:
+ moveToCity(kVienna, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBudapest:
+ moveToCity(kBudapest, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuBelgrade:
+ moveToCity(kBelgrade, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuConstantinople:
+ moveToCity(kConstantinople, clicked);
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot decrease volume further
+ if (getVolume() == 0) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeDown, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseVolume:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase volume further
+ if (getVolume() >= 7) {
+ showFrame(kOverlayButtons, kButtonVolume, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipVolumeUp, true);
+
+ // Show highlight on button & adjust volume if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonVolumeUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setVolume(getVolume() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ uint32 nextFrameCount = getFrameCount() + 15;
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ getSound()->updateQueue();
+ }
+ } else {
+ showFrame(kOverlayButtons, kButtonVolumeUp, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuDecreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() == 0) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() - 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessDown, false);
+ showFrame(kOverlayButtons, kButtonBrightnessDownPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessDown, true);
+ }
+ break;
+
+ //////////////////////////////////////////////////////////////////////////
+ case kMenuIncreaseBrightness:
+ if (hasTimeDelta()) {
+ hideOverlays();
+ break;
+ }
+
+ // Cannot increase brightness further
+ if (getBrightness() >= 6) {
+ showFrame(kOverlayButtons, kButtonBrightness, true);
+ showFrame(kOverlayTooltip, -1, true);
+ break;
+ }
+
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, true);
+
+ // Show highlight on button & adjust brightness if needed
+ if (clicked) {
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ setBrightness(getBrightness() + 1);
+
+ getSaveLoad()->saveVolumeBrightness();
+
+ // Reshow the background and frames (they will pick up the new brightness through the GraphicsManager)
+ _engine->getGraphicsManager()->draw(getScenes()->get((SceneIndex)(_isGameStarted ? _gameId * 5 + 1 : _gameId * 5 + 2)), GraphicsManager::kBackgroundC, true);
+ showFrame(kOverlayTooltip, kTooltipBrightnessUp, false);
+ showFrame(kOverlayButtons, kButtonBrightnessUpPushed, false);
+ } else {
+ showFrame(kOverlayButtons, kButtonBrightnessUp, true);
+ }
+ break;
+ }
+
+ return true;
+}
+
+void Menu::setLogicEventHandlers() {
+ SET_EVENT_HANDLERS(Logic, getLogic());
+ clear();
+ _isShowingMenu = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related
+//////////////////////////////////////////////////////////////////////////
+void Menu::init(bool doSavegame, SavegameType type, uint32 value) {
+
+ bool useSameIndex = true;
+
+ if (getGlobalTimer()) {
+ value = 0;
+
+ // Check if the CD file is present
+ ArchiveIndex index = kArchiveCd1;
+ switch (getProgress().chapter) {
+ default:
+ case kChapter1:
+ break;
+
+ case kChapter2:
+ case kChapter3:
+ index = kArchiveCd2;
+ break;
+
+ case kChapter4:
+ case kChapter5:
+ index = kArchiveCd3;
+ break;
+ }
+
+ if (ResourceManager::isArchivePresent(index)) {
+ setGlobalTimer(0);
+ useSameIndex = false;
+
+ // TODO remove existing savegame and reset index & savegame name
+ warning("Menu::initGame: not implemented!");
+ }
+
+ doSavegame = false;
+ } else {
+ // TODO rename saves?
+ }
+
+ // Create a new savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ getSaveLoad()->create(_gameId);
+
+ if (doSavegame)
+ getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone);
+
+ if (!getGlobalTimer()) {
+ // TODO: remove existing savegame temp file
+ }
+
+ // Init savegame & menu values
+ _lastIndex = getSaveLoad()->init(_gameId, true);
+ _lowerTime = getSaveLoad()->getTime(_lastIndex);
+
+ if (useSameIndex)
+ _index = _lastIndex;
+
+ //if (!getGlobalTimer())
+ // _index3 = 0;
+
+ if (!getProgress().chapter)
+ getProgress().chapter = kChapter1;
+
+ getState()->time = (TimeValue)getSaveLoad()->getTime(_index);
+ getProgress().chapter = getSaveLoad()->getChapter(_index);
+
+ if (_lowerTime >= kTimeStartGame) {
+ _currentTime = (uint32)getState()->time;
+ _time = (uint32)getState()->time;
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+
+ initTime(type, value);
+ }
+}
+
+// Start a game (or load an existing savegame)
+void Menu::startGame() {
+ // Clear savegame headers
+ getSaveLoad()->clear();
+
+ // Hide menu elements
+ _clock->clear();
+ _trainLine->clear();
+
+ if (_lastIndex == _index) {
+ setGlobalTimer(0);
+ if (_index) {
+ getSaveLoad()->loadGame(_gameId);
+ } else {
+ getLogic()->resetState();
+ getEntities()->setup(true, kEntityPlayer);
+ }
+ } else {
+ getSaveLoad()->loadGame(_gameId, _index);
+ }
+}
+
+// Switch to the next savegame
+void Menu::switchGame() {
+
+ // Switch back to blue game is the current game is not started
+ _gameId = SaveLoad::isSavegameValid(_gameId) ? getNextGameId() : kGameBlue;
+
+ // Initialize savegame if needed
+ if (!SaveLoad::isSavegamePresent(_gameId))
+ getSaveLoad()->create(_gameId);
+
+ getState()->time = kTimeNone;
+
+ // Clear menu elements
+ _clock->clear();
+ _trainLine->clear();
+
+ // Clear loaded savegame data
+ getSaveLoad()->clear(true);
+
+ init(false, kSavegameTypeIndex, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Overlays & elements
+//////////////////////////////////////////////////////////////////////////
+void Menu::checkHotspots() {
+ if (!_isShowingMenu)
+ return;
+
+ if (!getFlags()->shouldRedraw)
+ return;
+
+ if (_isShowingCredits)
+ return;
+
+ SceneHotspot *hotspot = NULL;
+ getScenes()->get(getState()->scene)->checkHotSpot(getCoords(), &hotspot);
+
+ if (hotspot)
+ handleEvent((StartMenuAction)hotspot->action, _mouseFlags);
+ else
+ hideOverlays();
+}
+
+void Menu::hideOverlays() {
+ _lastHotspot = NULL;
+
+ // Hide all menu overlays
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ showFrame(it->_key, -1, false);
+
+ getScenes()->drawFrames(true);
+}
+
+void Menu::showFrame(StartMenuOverlay overlayType, int index, bool redraw) {
+ if (index == -1) {
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ } else {
+ // Check that the overlay is valid
+ if (!_frames[overlayType])
+ return;
+
+ // Remove the frame and add a new one with the proper index
+ getScenes()->removeFromQueue(_frames[overlayType]);
+ _frames[overlayType]->setFrame((uint16)index);
+ getScenes()->addToQueue(_frames[overlayType]);
+ }
+
+ if (redraw)
+ getScenes()->drawFrames(true);
+}
+
+// Remove all frames from the queue
+void Menu::clear() {
+ for (MenuFrames::iterator it = _frames.begin(); it != _frames.end(); it++)
+ getScenes()->removeAndRedraw(&it->_value, false);
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+}
+
+// Get the sequence name to use for the acorn highlight, depending of the currently loaded savegame
+Common::String Menu::getAcornSequenceName(GameId id) const {
+ Common::String name = "";
+ switch (id) {
+ default:
+ case kGameBlue:
+ name = "aconblu3.seq";
+ break;
+
+ case kGameRed:
+ name = "aconred.seq";
+ break;
+
+ case kGameGreen:
+ name = "acongren.seq";
+ break;
+
+ case kGamePurple:
+ name = "aconpurp.seq";
+ break;
+
+ case kGameTeal:
+ name = "aconteal.seq";
+ break;
+
+ case kGameGold:
+ name = "acongold.seq";
+ break;
+ }
+
+ return name;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Time
+//////////////////////////////////////////////////////////////////////////
+void Menu::initTime(SavegameType type, uint32 value) {
+ if (!value)
+ return;
+
+ // The savegame entry index
+ uint32 entryIndex = 0;
+
+ switch (type) {
+ default:
+ break;
+
+ case kSavegameTypeIndex:
+ entryIndex = (_index <= value) ? 1 : _index - value;
+ break;
+
+ case kSavegameTypeTime:
+ if (value < kTimeStartGame)
+ break;
+
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ // Iterate through existing entries
+ do {
+ if (getSaveLoad()->getTime(entryIndex) <= value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent:
+ entryIndex = _index;
+ if (!entryIndex)
+ break;
+
+ do {
+ if (getSaveLoad()->getValue(entryIndex) == value)
+ break;
+
+ entryIndex--;
+ } while (entryIndex);
+ break;
+
+ case kSavegameTypeEvent2:
+ // TODO rewrite in a more legible way
+ if (_index > 1) {
+ uint32 index = _index;
+ do {
+ if (getSaveLoad()->getValue(index) == value)
+ break;
+
+ index--;
+ } while (index > 1);
+
+ entryIndex = index - 1;
+ } else {
+ entryIndex = _index - 1;
+ }
+ break;
+ }
+
+ if (entryIndex) {
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getTime(entryIndex));
+ }
+}
+
+void Menu::updateTime(uint32 time) {
+ if (_currentTime == _time)
+ _delta = 0;
+
+ _currentTime = time;
+
+ if (_time != time) {
+ if (getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ getSound()->playSoundWithSubtitles((_currentTime >= _time) ? "LIB042" : "LIB041", SoundManager::kFlagMenuClock, kEntityChapters);
+ adjustIndex(_currentTime, _time, false);
+ }
+}
+
+void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) {
+ uint32 index = 0;
+ int32 timeDelta = -1;
+
+ if (time1 != time2) {
+
+ index = _index;
+
+ if (time2 >= time1) {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if ((int32)_index >= 0) {
+ do {
+ // Calculate new delta
+ int32 newDelta = time1 - (uint32)getSaveLoad()->getTime(currentIndex);
+
+ if (newDelta >= 0 && timeDelta >= newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ --currentIndex;
+ } while ((int32)currentIndex >= 0);
+ }
+ } else {
+ index = _index - 1;
+ }
+ } else {
+ if (searchEntry) {
+ uint32 currentIndex = _index;
+
+ if (_lastIndex >= _index) {
+ do {
+ // Calculate new delta
+ int32 newDelta = (uint32)getSaveLoad()->getTime(currentIndex) - time1;
+
+ if (newDelta >= 0 && timeDelta > newDelta) {
+ timeDelta = newDelta;
+ index = currentIndex;
+ }
+
+ ++currentIndex;
+ } while (currentIndex <= _lastIndex);
+ }
+ } else {
+ index = _index + 1;
+ }
+ }
+
+ _index = index;
+ checkHotspots();
+ }
+
+ if (_index == _currentIndex) {
+ if (getProgress().chapter != getSaveLoad()->getChapter(index))
+ getProgress().chapter = getSaveLoad()->getChapter(_index);
+ }
+}
+
+void Menu::goToTime(uint32 time) {
+
+ uint32 entryIndex = 0;
+ uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getTime(0) - time));
+ uint32 index = 0;
+
+ do {
+ uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getTime(index) - time));
+ if (deltaTime2 < deltaTime) {
+ deltaTime = deltaTime2;
+ entryIndex = index;
+ }
+
+ ++index;
+ } while (_lastIndex >= index);
+
+ _currentIndex = entryIndex;
+ updateTime(getSaveLoad()->getTime(entryIndex));
+}
+
+void Menu::setTime() {
+ _currentIndex = _index;
+ _currentTime = getSaveLoad()->getTime(_currentIndex);
+
+ if (_time == _currentTime)
+ adjustTime();
+}
+
+void Menu::forwardTime() {
+ if (_lastIndex <= _index)
+ return;
+
+ _currentIndex = _lastIndex;
+ updateTime(getSaveLoad()->getTime(_currentIndex));
+}
+
+void Menu::rewindTime() {
+ if (!_index)
+ return;
+
+ _currentIndex = 0;
+ updateTime(getSaveLoad()->getTime(_currentIndex));
+}
+
+void Menu::adjustTime() {
+ uint32 originalTime = _time;
+
+ // Adjust time delta
+ Common::Rational timeDelta(_delta >= 90 ? 9 : (9 * _delta + 89), _delta >= 90 ? 1 : 90);
+
+ if (_currentTime < _time) {
+ timeDelta *= 900;
+ _time -= timeDelta.toInt();
+
+ if (_currentTime > _time)
+ _time = _currentTime;
+ } else {
+ timeDelta *= 900;
+ _time += timeDelta.toInt();
+
+ if (_currentTime < _time)
+ _time = _currentTime;
+ }
+
+ if (_currentTime == _time && getSound()->isBuffered(kEntityChapters))
+ getSound()->removeFromQueue(kEntityChapters);
+
+ _clock->draw(_time);
+ _trainLine->draw(_time);
+ getScenes()->drawFrames(true);
+
+ adjustIndex(_time, originalTime, true);
+
+ ++_delta;
+}
+
+void Menu::moveToCity(CityButton city, bool clicked) {
+ uint32 time = (uint32)_cityButtonsInfo[city].time;
+
+ // TODO Check if we have access (there seems to be more checks on some internal times) - probably : current_time (menu only) / game time / some other?
+ if (_lowerTime < time || _time == time || _currentTime == time) {
+ hideOverlays();
+ return;
+ }
+
+ // Show city overlay
+ showFrame((StartMenuOverlay)((_cityButtonsInfo[city].index >> 6) + 3), _cityButtonsInfo[city].index & 63, true);
+
+ if (clicked) {
+ showFrame(kOverlayTooltip, -1, true);
+ getSound()->playSound(kEntityPlayer, "LIB046");
+ goToTime(time);
+
+ _handleTimeDelta = true;
+
+ return;
+ }
+
+ // Special case of first and last cities
+ if (city == kParis || city == kConstantinople) {
+ showFrame(kOverlayTooltip, (city == kParis) ? kTooltipRewindParis : kTooltipForwardConstantinople, true);
+ return;
+ }
+
+ showFrame(kOverlayTooltip, (_time <= time) ? _cityButtonsInfo[city].forward : _cityButtonsInfo[city].rewind, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound / Brightness
+//////////////////////////////////////////////////////////////////////////
+
+// Get current volume (converted internal ScummVM value)
+uint32 Menu::getVolume() const {
+ return getState()->volume;
+}
+
+// Set the volume (converts to ScummVM values)
+void Menu::setVolume(uint32 volume) const {
+ getState()->volume = volume;
+
+ // Clamp volume
+ uint32 value = volume * Audio::Mixer::kMaxMixerVolume / 7;
+
+ if (value > Audio::Mixer::kMaxMixerVolume)
+ value = Audio::Mixer::kMaxMixerVolume;
+
+ _engine->_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, (int32)value);
+}
+
+uint32 Menu::getBrightness() const {
+ return getState()->brightness;
+}
+
+void Menu::setBrightness(uint32 brightness) const {
+ getState()->brightness = brightness;
+
+ // TODO reload cursor & font with adjusted brightness
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/menu.h b/engines/lastexpress/game/menu.h
new file mode 100644
index 0000000000..765a611c41
--- /dev/null
+++ b/engines/lastexpress/game/menu.h
@@ -0,0 +1,210 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_MENU_H
+#define LASTEXPRESS_MENU_H
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/eventhandler.h"
+
+#include "lastexpress/shared.h"
+
+#include "common/hashmap.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class Scene;
+class SceneHotspot;
+
+class Clock;
+class TrainLine;
+
+class Menu : public EventHandler {
+public:
+ Menu(LastExpressEngine *engine);
+ ~Menu();
+
+ void show(bool doSavegame, SavegameType type, uint32 value);
+
+ // Event handling
+ void eventMouse(const Common::Event &ev);
+ void eventTick(const Common::Event &ev);
+
+ bool isShown() const { return _isShowingMenu; }
+
+ GameId getGameId() const { return _gameId; }
+
+private:
+ // Start menu events
+ enum StartMenuAction {
+ kMenuContinue = 1,
+ kMenuCredits = 2,
+ kMenuQuitGame = 3,
+ kMenuCase4 = 4,
+ kMenuSwitchSaveGame = 6,
+ kMenuRewindGame = 7,
+ kMenuForwardGame = 8,
+ kMenuParis = 10,
+ kMenuStrasBourg = 11,
+ kMenuMunich = 12,
+ kMenuVienna = 13,
+ kMenuBudapest = 14,
+ kMenuBelgrade = 15,
+ kMenuConstantinople = 16,
+ kMenuDecreaseVolume = 17,
+ kMenuIncreaseVolume = 18,
+ kMenuDecreaseBrightness = 19,
+ kMenuIncreaseBrightness = 20
+ };
+
+ // City buttons
+ enum CityButton {
+ kParis = 0,
+ kStrasbourg = 1,
+ kMunich = 2,
+ kVienna = 3,
+ kBudapest = 4,
+ kBelgrade = 5,
+ kConstantinople = 6
+ };
+
+ // Start menu overlay elements
+ enum StartMenuOverlay {
+ kOverlayTooltip, // 0
+ kOverlayEggButtons,
+ kOverlayButtons,
+ kOverlayAcorn,
+ kOverlayCity1,
+ kOverlayCity2, // 5
+ kOverlayCity3,
+ kOverlayCredits
+ };
+
+ LastExpressEngine *_engine;
+
+ // Sequences
+ Sequence *_seqTooltips;
+ Sequence *_seqEggButtons;
+ Sequence *_seqButtons;
+ Sequence *_seqAcorn;
+ Sequence *_seqCity1;
+ Sequence *_seqCity2;
+ Sequence *_seqCity3;
+ Sequence *_seqCredits;
+
+ GameId _gameId;
+
+ // Indicator to know if we need to show the start animation when showMenu is called
+ bool _hasShownStartScreen;
+ bool _hasShownIntro;
+
+ bool _isShowingCredits;
+ bool _isGameStarted;
+ bool _isShowingMenu;
+
+
+ uint16 _creditsSequenceIndex;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Event handling
+ uint32 _checkHotspotsTicks;
+ Common::EventType _mouseFlags;
+ SceneHotspot *_lastHotspot;
+
+ void init(bool doSavegame, SavegameType type, uint32 value);
+ void setup();
+ bool handleEvent(StartMenuAction action, Common::EventType type);
+ void checkHotspots();
+ void setLogicEventHandlers();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Game-related
+ void startGame();
+ void switchGame();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Overlays & elements
+ Clock *_clock;
+ TrainLine *_trainLine;
+
+ struct MenuOverlays_EqualTo {
+ bool operator()(const StartMenuOverlay &x, const StartMenuOverlay &y) const { return x == y; }
+ };
+
+ struct MenuOverlays_Hash {
+ uint operator()(const StartMenuOverlay &x) const { return x; }
+ };
+
+ typedef Common::HashMap<StartMenuOverlay, SequenceFrame *, MenuOverlays_Hash, MenuOverlays_EqualTo> MenuFrames;
+
+ MenuFrames _frames;
+
+ void hideOverlays();
+ void showFrame(StartMenuOverlay overlay, int index, bool redraw);
+
+ void clear();
+
+ // TODO: remove?
+ void moveToCity(CityButton city, bool clicked);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Misc
+ Common::String getAcornSequenceName(GameId id) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Time
+ uint32 _currentTime; // current game time
+ uint32 _lowerTime; // lower time value
+ uint32 _time;
+
+ uint32 _currentIndex; // current savegame entry
+ uint32 _index;
+ uint32 _lastIndex;
+ uint32 _delta;
+ bool _handleTimeDelta;
+
+ void initTime(SavegameType type, uint32 val);
+ void updateTime(uint32 time);
+ void adjustTime();
+ void adjustIndex(uint32 time1, uint32 time2, bool searchEntry);
+ void goToTime(uint32 time);
+ void setTime();
+ void forwardTime();
+ void rewindTime();
+ bool hasTimeDelta() { return (_currentTime - _time) >= 1; }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sound/Brightness related
+ uint32 getVolume() const;
+ void setVolume(uint32 volume) const;
+ uint32 getBrightness() const;
+ void setBrightness(uint32 brightness) const;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_MENU_H
diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp
new file mode 100644
index 0000000000..f0e81781b6
--- /dev/null
+++ b/engines/lastexpress/game/object.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$
+ *
+ */
+
+#include "lastexpress/game/object.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+Common::String Objects::Object::toString() {
+ return Common::String::printf("{ %s - %d - %d - %d - %d }", ENTITY_NAME(entity), location, cursor, cursor2, location2);
+}
+
+Objects::Objects(LastExpressEngine *engine) : _engine(engine) {}
+
+const Objects::Object Objects::get(ObjectIndex index) const {
+ if (index >= kObjectMax)
+ error("Objects::get - internal error: invalid object index (%d)", index);
+
+ return _objects[index];
+}
+
+void Objects::update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2) {
+ if (index >= kObjectMax)
+ return;
+
+ Object *object = &_objects[index];
+
+ // Store original location
+ ObjectLocation original_location = object->location;
+
+ // Update entity
+ object->entity = entity;
+ object->location = location;
+
+ if (cursor != kCursorKeepValue || cursor2 != kCursorKeepValue) {
+ if (cursor != kCursorKeepValue)
+ object->cursor = cursor;
+ if (cursor2 != kCursorKeepValue)
+ object->cursor2 = cursor2;
+
+ getLogic()->updateCursor();
+ }
+
+ getFlags()->flag_3 = true;
+
+ // Compartments
+ if (original_location != location && (original_location == kObjectLocation2 || location == kObjectLocation2))
+ if ((index >= kObjectCompartment1 && index <= kObjectCompartment8)
+ || (index >= kObjectCompartmentA && index <= kObjectCompartmentF)) {
+ getScenes()->updateDoorsAndClock();
+ }
+}
+
+void Objects::updateLocation2(ObjectIndex index, ObjectLocation location2) {
+ if (index >= kObjectMax)
+ return;
+
+ _objects[index].location2 = location2;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void Objects::saveLoadWithSerializer(Common::Serializer &s) {
+ for (int i = 0; i < ARRAYSIZE(_objects); i++)
+ _objects[i].saveLoadWithSerializer(s);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String Objects::toString() {
+ Common::String ret = "";
+
+ for (int i = 0; i < ARRAYSIZE(_objects); i++)
+ ret += Common::String::printf("%d : %s\n", i, _objects[i].toString().c_str());
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/object.h b/engines/lastexpress/game/object.h
new file mode 100644
index 0000000000..96af4f07a6
--- /dev/null
+++ b/engines/lastexpress/game/object.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_OBJECT_H
+#define LASTEXPRESS_OBJECT_H
+
+#include "lastexpress/shared.h"
+
+#include "common/serializer.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Objects : Common::Serializable {
+public:
+
+ struct Object : Common::Serializable { // All fields should be saved as bytes
+ EntityIndex entity;
+ ObjectLocation location;
+ CursorStyle cursor;
+ CursorStyle cursor2;
+ ObjectLocation location2;
+
+ Object() {
+ entity = kEntityPlayer;
+ location = kObjectLocationNone;
+ cursor = kCursorHandKnock;
+ cursor2 = kCursorHandKnock;
+ location2 = kObjectLocationNone;
+ }
+
+ Common::String toString();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsByte(entity);
+ s.syncAsByte(location);
+ s.syncAsByte(cursor);
+ s.syncAsByte(cursor2);
+ s.syncAsByte(location2);
+ }
+ };
+
+ Objects(LastExpressEngine *engine);
+
+ const Object get(ObjectIndex index) const;
+ void update(ObjectIndex index, EntityIndex entity, ObjectLocation location, CursorStyle cursor, CursorStyle cursor2);
+ void updateLocation2(ObjectIndex index, ObjectLocation location2);
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+private:
+ LastExpressEngine *_engine;
+
+ Object _objects[kObjectMax];
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_OBJECT_H
diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp
new file mode 100644
index 0000000000..224d91eddd
--- /dev/null
+++ b/engines/lastexpress/game/savegame.cpp
@@ -0,0 +1,564 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/savegame.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/debug.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/helpers.h"
+
+#include "common/file.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+// Names of savegames
+static const struct {
+ const char *saveFile;
+} gameInfo[6] = {
+ {"blue.egg"},
+ {"red.egg"},
+ {"green.egg"},
+ {"purple.egg"},
+ {"teal.egg"},
+ {"gold.egg"}
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Constructors
+//////////////////////////////////////////////////////////////////////////
+
+SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) {
+}
+
+SaveLoad::~SaveLoad() {
+ clear(true);
+
+ //Zero passed pointers
+ _engine = NULL;
+}
+
+void SaveLoad::initStream() {
+ delete _savegame;
+ _savegame = new SavegameStream();
+}
+
+void SaveLoad::flushStream(GameId id) {
+ Common::OutSaveFile *save = openForSaving(id);
+ if (!save)
+ error("SaveLoad::flushStream: cannot open savegame (%s)!", getFilename(id).c_str());
+
+ if (!_savegame)
+ error("SaveLoad::flushStream: savegame stream is invalid");
+
+ save->write(_savegame->getData(), (uint32)_savegame->size());
+
+ delete save;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Init
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::create(GameId id) {
+ initStream();
+
+ Common::Serializer ser(NULL, _savegame);
+ SavegameMainHeader header;
+ header.saveLoadWithSerializer(ser);
+
+ flushStream(id);
+}
+
+uint32 SaveLoad::init(GameId id, bool resetHeaders) {
+ initStream();
+
+ // Load game data
+ loadStream(id);
+
+ // Get the main header
+ Common::Serializer ser(_savegame, NULL);
+ SavegameMainHeader mainHeader;
+ mainHeader.saveLoadWithSerializer(ser);
+ if (!mainHeader.isValid())
+ error("SaveLoad::init - Savegame seems to be corrupted (invalid header)");
+
+ // Reset cached entry headers if needed
+ if (resetHeaders) {
+ clear();
+
+ SavegameEntryHeader *entryHeader = new SavegameEntryHeader();
+ entryHeader->time = kTimeCityParis;
+ entryHeader->chapter = kChapter1;
+
+ _gameHeaders.push_back(entryHeader);
+ }
+
+ // Read the list of entry headers
+ if (_savegame->size() > 32) {
+ while (!_savegame->eos() && !_savegame->err()) {
+
+ // Update sound queue while we go through the savegame
+ getSound()->updateQueue();
+
+ SavegameEntryHeader *entry = new SavegameEntryHeader();
+ entry->saveLoadWithSerializer(ser);
+
+ if (!entry->isValid())
+ break;
+
+ _gameHeaders.push_back(entry);
+
+ _savegame->seek(entry->offset, SEEK_CUR);
+ }
+ }
+
+ // return the index to the current save game entry (we store count + 1 entries, so we're good)
+ return mainHeader.count;
+}
+
+void SaveLoad::loadStream(GameId id) {
+ Common::InSaveFile *save = openForLoading(id);
+ if (save->size() < 32)
+ error("SaveLoad::init - Savegame seems to be corrupted (not enough data: %i bytes)", save->size());
+
+ if (!_savegame)
+ error("SaveLoad::loadStream: savegame stream is invalid");
+
+ // Load all savegame data
+ uint8* buf = new uint8[8192];
+ while (!save->eos() && !save->err()) {
+ uint32 count = save->read(buf, sizeof(buf));
+ if (count) {
+ uint32 w = _savegame->write(buf, count);
+ assert (w == count);
+ }
+ }
+
+ if (save->err())
+ error("SaveLoad::init - Error reading savegame");
+
+ delete[] buf;
+ delete save;
+
+ // Move back to the beginning of the stream
+ _savegame->seek(0);
+}
+
+void SaveLoad::clear(bool clearStream) {
+ for (uint i = 0; i < _gameHeaders.size(); i++)
+ SAFE_DELETE(_gameHeaders[i]);
+
+ _gameHeaders.clear();
+
+ if (clearStream)
+ SAFE_DELETE(_savegame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Save & Load
+//////////////////////////////////////////////////////////////////////////
+
+// Load game
+void SaveLoad::loadGame(GameId id) {
+ // Rewind current savegame
+ _savegame->seek(0);
+
+ // Validate main header
+ SavegameMainHeader header;
+ if (!loadMainHeader(_savegame, &header)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str());
+ return;
+ }
+
+ // Load the last entry
+ _savegame->seek(header.offsetEntry);
+
+ SavegameType type = kSavegameTypeIndex;
+ EntityIndex entity = kEntityPlayer;
+ uint32 val = 0;
+ readEntry(&type, &entity, &val, header.keepIndex == 1);
+
+ // Setup last loading time
+ _gameTicksLastSavegame = getState()->timeTicks;
+
+ if (header.keepIndex) {
+ getSound()->clearQueue();
+
+ readEntry(&type, &entity, &val, false);
+ }
+
+ getEntities()->reset();
+ getEntities()->setup(false, entity);
+}
+
+// Load a specific game entry
+void SaveLoad::loadGame(GameId id, uint32 index) {
+ error("SaveLoad::loadGame: not implemented! (only loading the last entry is working for now)");
+}
+
+// Save game
+void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
+ if (getState()->scene <= kSceneIntro)
+ return;
+
+ // Validate main header
+ SavegameMainHeader header;
+ if (!loadMainHeader(_savegame, &header)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str());
+ return;
+ }
+
+ if (!_savegame)
+ error("SaveLoad::saveGame: savegame stream is invalid");
+
+ // Validate the current entry if it exists
+ if (header.count > 0) {
+ _savegame->seek(header.offsetEntry);
+
+ // Load entry header
+ SavegameEntryHeader entry;
+ Common::Serializer ser(_savegame, NULL);
+ entry.saveLoadWithSerializer(ser);
+
+ if (!entry.isValid()) {
+ warning("SaveLoad::saveGame: Invalid entry. This savegame might be corrupted!");
+ _savegame->seek(header.offset);
+ } else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) {
+ // Not ready to save a game, skipping!
+ return;
+ } else if ((type == kSavegameTypeTime || type == kSavegameTypeEvent)
+ && (entry.type == kSavegameTypeTickInterval && (getState()->time - entry.time) < 450)) {
+ _savegame->seek(header.offsetEntry);
+ --header.count;
+ } else {
+ _savegame->seek(header.offset);
+ }
+ } else {
+ // Seek to the next savegame entry
+ _savegame->seek(header.offset);
+ }
+
+ if (type != kSavegameTypeEvent2 && type != kSavegameTypeAuto)
+ header.offsetEntry = (uint32)_savegame->pos();
+
+ // Write the savegame entry
+ writeEntry(type, entity, value);
+
+ if (!header.keepIndex)
+ ++header.count;
+
+ if (type == kSavegameTypeEvent2 || type == kSavegameTypeAuto) {
+ header.keepIndex = 1;
+ } else {
+ header.keepIndex = 0;
+ header.offset = (uint32)_savegame->pos();
+
+ // Save ticks
+ _gameTicksLastSavegame = getState()->timeTicks;
+ }
+
+ // Validate the main header
+ if (!header.isValid())
+ error("SaveLoad::saveGame: main game header is invalid!");
+
+ // Write the main header
+ _savegame->seek(0);
+ Common::Serializer ser(NULL, _savegame);
+ header.saveLoadWithSerializer(ser);
+
+ flushStream(getMenu()->getGameId());
+}
+
+void SaveLoad::saveVolumeBrightness() {
+ warning("SaveLoad::saveVolumeBrightness: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Headers
+//////////////////////////////////////////////////////////////////////////
+bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header) {
+ if (!stream)
+ return false;
+
+ // Check there is enough data (32 bytes)
+ if (stream->size() < 32) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes)!", stream->size());
+ return false;
+ }
+
+ // Rewind stream
+ stream->seek(0);
+
+ Common::Serializer ser(stream, NULL);
+ header->saveLoadWithSerializer(ser);
+
+ // Validate the header
+ if (!header->isValid()) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header!");
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entries
+//////////////////////////////////////////////////////////////////////////
+void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
+#define WRITE_ENTRY(name, func, val) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \
+ if (_count != val)\
+ error("SaveLoad::writeEntry: Number of bytes written (%d) differ from expected count (%d)", _count, val); \
+}
+
+ if (!_savegame)
+ error("SaveLoad::writeEntry: savegame stream is invalid");
+
+ SavegameEntryHeader header;
+
+ header.type = type;
+ header.time = (uint32)getState()->time;
+ header.chapter = getProgress().chapter;
+ header.value = value;
+
+ // Save position
+ uint32 originalPosition = (uint32)_savegame->pos();
+
+ // Write header
+ Common::Serializer ser(NULL, _savegame);
+ header.saveLoadWithSerializer(ser);
+
+ // Write game data
+ WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4);
+ WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4);
+ WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4);
+ WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000);
+ WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2);
+ WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128);
+ WRITE_ENTRY("events", getState()->syncEvents(ser), 512);
+ WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32);
+ WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128);
+ WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40);
+ WRITE_ENTRY("sound", getSound()->saveLoadWithSerializer(ser), 3 * 4 + getSound()->count() * 64);
+ WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16);
+
+ header.offset = (uint32)_savegame->pos() - (originalPosition + 32);
+
+ // Add padding if necessary
+ while (header.offset & 0xF) {
+ _savegame->writeByte(0);
+ header.offset++;
+ }
+
+ // Save end position
+ uint32 endPosition = (uint32)_savegame->pos();
+
+ // Validate entry header
+ if (!header.isValid())
+ error("SaveLoad::writeEntry: entry header is invalid");
+
+ // Save the header with the updated info
+ _savegame->seek(originalPosition);
+ header.saveLoadWithSerializer(ser);
+
+ // Move back to the end of the entry
+ _savegame->seek(endPosition);
+}
+
+void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) {
+#define LOAD_ENTRY(name, func, val) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \
+ if (_count != val) \
+ error("SaveLoad::readEntry: Number of bytes read (%d) differ from expected count (%d)", _count, val); \
+}
+
+#define LOAD_ENTRY_ONLY(name, func) { \
+ uint32 _prevPosition = (uint32)_savegame->pos(); \
+ func; \
+ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
+ debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \
+}
+
+ if (!type || !entity || !val)
+ error("SaveLoad::readEntry: Invalid parameters passed!");
+
+ // Load entry header
+ SavegameEntryHeader entry;
+ Common::Serializer ser(_savegame, NULL);
+ entry.saveLoadWithSerializer(ser);
+
+ if (!entry.isValid())
+ error("SaveLoad::readEntry: entry header is invalid!");
+
+ // Init type, entity & value
+ *type = entry.type;
+ *val = entry.value;
+
+ // Save position
+ uint32 originalPosition = (uint32)_savegame->pos();
+
+ // Load game data
+ LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4);
+ LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4);
+ LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4);
+ LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000);
+ LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2);
+ LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128);
+ LOAD_ENTRY("events", getState()->syncEvents(ser), 512);
+ LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32);
+ LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128);
+ LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40);
+ LOAD_ENTRY_ONLY("sound", getSound()->saveLoadWithSerializer(ser));
+ LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser));
+
+ // Update chapter
+ getProgress().chapter = entry.chapter;
+
+ // Skip padding
+ uint32 offset = _savegame->pos() - originalPosition;
+ if (offset & 0xF) {
+ _savegame->seek((~offset & 0xF) + 1, SEEK_SET);
+ }
+}
+
+SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
+ if (index >= _gameHeaders.size())
+ error("SaveLoad::getEntry: invalid index (was:%d, max:%d)", index, _gameHeaders.size() - 1);
+
+ return _gameHeaders[index];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+
+// Check if a specific savegame exists
+bool SaveLoad::isSavegamePresent(GameId id) {
+ if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0)
+ return false;
+
+ return true;
+}
+
+// Check if the game has been started in the specific savegame
+bool SaveLoad::isSavegameValid(GameId id) {
+ if (!isSavegamePresent(id)) {
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getFilename(id).c_str());
+ return false;
+ }
+
+ SavegameMainHeader header;
+
+ Common::InSaveFile *save = openForLoading(id);
+ return loadMainHeader(save, &header);
+}
+
+bool SaveLoad::isGameFinished(uint32 menuIndex, uint32 savegameIndex) {
+ SavegameEntryHeader *data = getEntry(menuIndex);
+
+ if (savegameIndex != menuIndex)
+ return false;
+
+ if (data->type != kSavegameTypeEvent)
+ return false;
+
+ return (data->value == kEventAnnaKilled
+ || data->value == kEventKronosHostageAnnaNoFirebird
+ || data->value == kEventKahinaPunchBaggageCarEntrance
+ || data->value == kEventKahinaPunchBlue
+ || data->value == kEventKahinaPunchYellow
+ || data->value == kEventKahinaPunchSalon
+ || data->value == kEventKahinaPunchKitchen
+ || data->value == kEventKahinaPunchBaggageCar
+ || data->value == kEventKahinaPunchCar
+ || data->value == kEventKahinaPunchSuite4
+ || data->value == kEventKahinaPunchRestaurant
+ || data->value == kEventKahinaPunch
+ || data->value == kEventKronosGiveFirebird
+ || data->value == kEventAugustFindCorpse
+ || data->value == kEventMertensBloodJacket
+ || data->value == kEventMertensCorpseFloor
+ || data->value == kEventMertensCorpseBed
+ || data->value == kEventCoudertBloodJacket
+ || data->value == kEventGendarmesArrestation
+ || data->value == kEventAbbotDrinkGiveDetonator
+ || data->value == kEventMilosCorpseFloor
+ || data->value == kEventLocomotiveAnnaStopsTrain
+ || data->value == kEventTrainStopped
+ || data->value == kEventCathVesnaRestaurantKilled
+ || data->value == kEventCathVesnaTrainTopKilled
+ || data->value == kEventLocomotiveConductorsDiscovered
+ || data->value == kEventViennaAugustUnloadGuns
+ || data->value == kEventViennaKronosFirebird
+ || data->value == kEventVergesAnnaDead
+ || data->value == kEventTrainExplosionBridge
+ || data->value == kEventKronosBringNothing);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Private methods
+//////////////////////////////////////////////////////////////////////////
+
+// Get the file name from the savegame ID
+Common::String SaveLoad::getFilename(GameId id) {
+ if (id >= 6)
+ error("SaveLoad::getName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id);
+
+ return gameInfo[id].saveFile;
+}
+
+Common::InSaveFile *SaveLoad::openForLoading(GameId id) {
+ Common::InSaveFile *load = g_system->getSavefileManager()->openForLoading(getFilename(id));
+
+ if (!load)
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForLoading - Cannot open savegame for loading: %s", getFilename(id).c_str());
+
+ return load;
+}
+
+Common::OutSaveFile *SaveLoad::openForSaving(GameId id) {
+ Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id));
+
+ if (!save)
+ debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForSaving - Cannot open savegame for writing: %s", getFilename(id).c_str());
+
+ return save;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h
new file mode 100644
index 0000000000..d3ba5d13af
--- /dev/null
+++ b/engines/lastexpress/game/savegame.h
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SAVELOAD_H
+#define LASTEXPRESS_SAVELOAD_H
+
+/*
+ Savegame format
+ ---------------
+
+ header: 32 bytes
+ uint32 {4} - signature: 0x12001200
+ uint32 {4} - chapter - needs to be [0; 5]
+ uint32 {4} - time - needs to be >= 32 [1061100; timeMax]
+ uint32 {4} - ?? needs to be >= 32
+ uint32 {4} - ?? needs to be = 1
+ uint32 {4} - Brightness (needs to be [0-6])
+ uint32 {4} - Volume (needs to be [0-7])
+ uint32 {4} - ?? needs to be = 9
+
+ Game data Format
+ -----------------
+
+ uint32 {4} - entity
+ uint32 {4} - current time
+ uint32 {4} - time delta (how much a tick is in "real" time)
+ uint32 {4} - time ticks
+ uint32 {4} - scene Index max: 2500
+ byte {1} - use backup scene
+ uint32 {4} - backup Scene Index 1 max: 2500
+ uint32 {4} - backup Scene Index 2 max: 2500
+ uint32 {4} - selected inventory item max: 32
+ uint32 {4*100*10} - positions (by car)
+ uint32 {4*16} - compartments
+ uint32 {4*16} - compartments ??
+ uint32 {4*128} - game progress
+ byte {512} - game events
+ byte {7*32} - inventory
+ byte {5*128} - objects
+ byte {1262*40} - entities (characters and train entities)
+
+ uint32 {4} - sound queue state
+ uint32 {4} - ??
+ uint32 {4} - number of sound entries
+ byte {count*68} - sound entries
+
+ byte {16*128} - save point data
+ uint32 {4} - number of save points (max: 128)
+ byte {count*16} - save points
+
+ ... more unknown stuff
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "common/savefile.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+// Savegame signatures
+#define SAVEGAME_SIGNATURE 0x12001200
+#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660
+
+class LastExpressEngine;
+
+class SaveLoad {
+public:
+ SaveLoad(LastExpressEngine *engine);
+ ~SaveLoad();
+
+ // Init
+ void create(GameId id);
+ void clear(bool clearStream = false);
+ uint32 init(GameId id, bool resetHeaders);
+
+ // Save & Load
+ void loadGame(GameId id);
+ void loadGame(GameId id, uint32 index);
+ void saveGame(SavegameType type, EntityIndex entity, uint32 value);
+
+ void loadVolumeBrightness();
+ void saveVolumeBrightness();
+
+ // Getting information
+ static bool isSavegamePresent(GameId id);
+ static bool isSavegameValid(GameId id);
+
+ bool isGameFinished(uint32 menuIndex, uint32 savegameIndex);
+
+ // Accessors
+ uint32 getTime(uint32 index) { return getEntry(index)->time; }
+ ChapterIndex getChapter(uint32 index) { return getEntry(index)->chapter; }
+ uint32 getValue(uint32 index) { return getEntry(index)->value; }
+ uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; }
+
+private:
+ class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
+ public:
+ SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES),
+ _eos(false) {}
+
+ int32 pos() const { return MemoryWriteStreamDynamic::pos(); }
+ int32 size() const { return MemoryWriteStreamDynamic::size(); }
+ bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); }
+ bool eos() const { return _eos; }
+ uint32 read(void *dataPtr, uint32 dataSize) {
+ if ((int32)dataSize > size() - pos()) {
+ dataSize = size() - pos();
+ _eos = true;
+ }
+ memcpy(dataPtr, getData() + pos(), dataSize);
+
+ seek(dataSize, SEEK_CUR);
+
+ return dataSize;
+ }
+ private:
+ bool _eos;
+ };
+
+ LastExpressEngine *_engine;
+
+ struct SavegameMainHeader : Common::Serializable {
+ uint32 signature;
+ uint32 count;
+ uint32 offset;
+ uint32 offsetEntry;
+ uint32 keepIndex;
+ int32 brightness;
+ int32 volume;
+ uint32 field_1C;
+
+ SavegameMainHeader() {
+ signature = SAVEGAME_SIGNATURE;
+ count = 0;
+ offset = 32;
+ offsetEntry = 32;
+ keepIndex = 0;
+ brightness = 3;
+ volume = 7;
+ field_1C = 9;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(signature);
+ s.syncAsUint32LE(count);
+ s.syncAsUint32LE(offset);
+ s.syncAsUint32LE(offsetEntry);
+ s.syncAsUint32LE(keepIndex);
+ s.syncAsUint32LE(brightness);
+ s.syncAsUint32LE(volume);
+ s.syncAsUint32LE(field_1C);
+ }
+
+ bool isValid() {
+ if (signature != SAVEGAME_SIGNATURE)
+ return false;
+
+ /* Check not needed as it can never be < 0
+ if (header.chapter < 0)
+ return false;*/
+
+ if (offset < 32)
+ return false;
+
+ if (offsetEntry < 32)
+ return false;
+
+ if (keepIndex != 1 && keepIndex != 0)
+ return false;
+
+ if (brightness < 0 || brightness > 6)
+ return false;
+
+ if (volume < 0 || volume > 7)
+ return false;
+
+ if (field_1C != 9)
+ return false;
+
+ return true;
+ }
+ };
+
+ struct SavegameEntryHeader : Common::Serializable {
+ uint32 signature;
+ SavegameType type;
+ uint32 time;
+ int offset;
+ ChapterIndex chapter;
+ uint32 value;
+ int field_18;
+ int field_1C;
+
+ SavegameEntryHeader() {
+ signature = SAVEGAME_ENTRY_SIGNATURE;
+ type = kSavegameTypeIndex;
+ time = kTimeNone;
+ offset = 0;
+ chapter = kChapterAll;
+ value = 0;
+ field_18 = 0;
+ field_1C = 0;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(signature);
+ s.syncAsUint32LE(type);
+ s.syncAsUint32LE(time);
+ s.syncAsUint32LE(offset);
+ s.syncAsUint32LE(chapter);
+ s.syncAsUint32LE(value);
+ s.syncAsUint32LE(field_18);
+ s.syncAsUint32LE(field_1C);
+ }
+
+ bool isValid() {
+ if (signature != SAVEGAME_ENTRY_SIGNATURE)
+ return false;
+
+ if (type < kSavegameTypeTime || type > kSavegameTypeTickInterval)
+ return false;
+
+ if (time < kTimeStartGame || time > kTimeCityConstantinople)
+ return false;
+
+ if (offset <= 0 || offset & 15)
+ return false;
+
+ /* No check for < 0, as it cannot happen normaly */
+ if (chapter == 0)
+ return false;
+
+ return true;
+ }
+ };
+
+ SavegameStream *_savegame;
+ Common::Array<SavegameEntryHeader *> _gameHeaders;
+ uint32 _gameTicksLastSavegame;
+
+ // Headers
+ static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header);
+
+ // Entries
+ void writeEntry(SavegameType type, EntityIndex entity, uint32 val);
+ void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex);
+
+ SavegameEntryHeader *getEntry(uint32 index);
+
+ // Opening save files
+ static Common::String getFilename(GameId id);
+ static Common::InSaveFile *openForLoading(GameId id);
+ static Common::OutSaveFile *openForSaving(GameId id);
+
+ // Savegame stream
+ void initStream();
+ void loadStream(GameId id);
+ void flushStream(GameId id);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVELOAD_H
diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp
new file mode 100644
index 0000000000..30467f8368
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.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 "lastexpress/game/savepoint.h"
+
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+
+
+namespace LastExpress {
+
+SavePoints::SavePoints(LastExpressEngine *engine) : _engine(engine) {
+ for (int i = 0; i < 40; i++)
+ _callbacks[i] = NULL;
+}
+
+SavePoints::~SavePoints() {
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savepoints
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ _savepoints.push_back(point);
+}
+
+void SavePoints::push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) {
+ if (_savepoints.size() >= _savePointsMaxSize)
+ return;
+
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ _savepoints.push_back(point);
+}
+
+SavePoint SavePoints::pop() {
+ SavePoint point = _savepoints.front();
+ _savepoints.pop_front();
+ return point;
+}
+
+
+void SavePoints::pushAll(EntityIndex entity, ActionIndex action, uint32 param) {
+ for (uint32 index = 1; index < 40; index++) {
+ if ((EntityIndex)index != entity)
+ push(entity, (EntityIndex)index, action, param);
+ }
+}
+
+// Process all savepoints
+void SavePoints::process() {
+ while (_savepoints.size() > 0 && getFlags()->isGameRunning) {
+ SavePoint savepoint = pop();
+
+ // If this is a data savepoint, update the entity
+ // otherwise, execute the callback
+ if (!updateEntityFromData(savepoint)) {
+
+ // Call requested callback
+ Entity::Callback *callback = getCallback(savepoint.entity1);
+ if (callback && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2));
+ (*callback)(savepoint);
+ }
+ }
+ }
+}
+
+void SavePoints::reset() {
+ _savepoints.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Data
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) {
+ if (_data.size() >= _savePointsMaxSize)
+ return;
+
+ SavePointData data;
+ data.entity1 = entity;
+ data.action = action;
+ data.param = param;
+
+ _data.push_back(data);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Callbacks
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) {
+ if (index >= 40)
+ error("SavePoints::setCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ if (!callback || !callback->isValid())
+ error("SavePoints::setCallback - attempting to set an invalid callback for entity %s", ENTITY_NAME(index));
+
+ _callbacks[index] = callback;
+}
+
+Entity::Callback *SavePoints::getCallback(EntityIndex index) const {
+ if (index >= 40)
+ error("SavePoints::getCallback - attempting to use an invalid entity index. Valid values 0-39, was %d", index);
+
+ return _callbacks[index];
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ point.param.intValue = param;
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const {
+ SavePoint point;
+ point.entity1 = entity1;
+ point.action = action;
+ point.entity2 = entity2;
+ strcpy((char *)&point.param.charValue, param);
+
+ Entity::Callback *callback = getCallback(entity1);
+ if (callback != NULL && callback->isValid()) {
+ debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param);
+ (*callback)(point);
+ }
+}
+
+void SavePoints::callAndProcess() {
+ SavePoint savepoint; // empty parameters
+
+ // We ignore the kEntityPlayer callback in the list
+ EntityIndex index = kEntityAnna;
+
+ // Call all callbacks with empty parameters
+ bool isRunning = getFlags()->isGameRunning;
+ while (isRunning) {
+
+ Entity::Callback *callback = getCallback(index);
+ if (callback != NULL && callback->isValid()) {
+ (*callback)(savepoint);
+ isRunning = getFlags()->isGameRunning;
+ }
+
+ index = (EntityIndex)(index + 1);
+
+ // Process all savepoints when done
+ if (index >= 40) {
+ if (isRunning)
+ process();
+
+ return;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+bool SavePoints::updateEntityFromData(const SavePoint &savepoint) {
+ for (int i = 0; i < (int)_data.size(); i++) {
+
+ // Not a data savepoint!
+ if (!_data[i].entity1)
+ return false;
+
+ // Found our data!
+ if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) {
+ debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param);
+
+ // the SavePoint param value is the index of the entity call parameter to update
+ getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Serializable
+//////////////////////////////////////////////////////////////////////////
+void SavePoints::saveLoadWithSerializer(Common::Serializer &s) {
+
+ // Serialize savepoint data
+ uint32 dataSize = (s.isLoading() ? _savePointsMaxSize : _data.size());
+ for (uint i = 0; i < dataSize; i++) {
+ if (s.isLoading()) {
+ SavePointData data;
+ _data.push_back(data);
+ }
+
+ s.syncAsUint32LE(_data[i].entity1);
+ s.syncAsUint32LE(_data[i].action);
+ s.syncAsUint32LE(_data[i].entity2);
+ s.syncAsUint32LE(_data[i].param);
+ }
+
+ // Skip uninitialized data if any
+ s.skip((_savePointsMaxSize - dataSize) * 16);
+
+ // Number of savepoints
+ uint32 numSavepoints = _savepoints.size();
+ s.syncAsUint32LE(numSavepoints);
+
+ // Savepoints
+ if (s.isLoading()) {
+ for (uint i = 0; i < numSavepoints; i++) {
+ SavePoint point;
+ s.syncAsUint32LE(point.entity1);
+ s.syncAsUint32LE(point.action);
+ s.syncAsUint32LE(point.entity2);
+ s.syncAsUint32LE(point.param.intValue);
+
+ _savepoints.push_back(point);
+
+ if (_savepoints.size() >= _savePointsMaxSize)
+ break;
+ }
+ } else {
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it) {
+ s.syncAsUint32LE((*it).entity1);
+ s.syncAsUint32LE((*it).action);
+ s.syncAsUint32LE((*it).entity2);
+ s.syncAsUint32LE((*it).param.intValue);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// toString
+//////////////////////////////////////////////////////////////////////////
+Common::String SavePoints::toString() {
+ Common::String ret = "";
+
+ ret += "Savepoint Data\n";
+ for (uint i = 0; i < _data.size(); i++)
+ ret += _data[i].toString() + "\n";
+
+ ret += "\nSavepoints\n";
+ for (Common::List<SavePoint>::iterator it = _savepoints.begin(); it != _savepoints.end(); ++it)
+ ret += (*it).toString() + "\n";
+
+ return ret;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h
new file mode 100644
index 0000000000..6b2c12751d
--- /dev/null
+++ b/engines/lastexpress/game/savepoint.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_SAVEPOINT_H
+#define LASTEXPRESS_SAVEPOINT_H
+
+#include "lastexpress/entities/entity.h"
+
+#include "lastexpress/helpers.h"
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/serializer.h"
+
+/*
+ Savepoint format
+ ----------------
+
+ Save point: max: 127 - FIFO list (ie. goes back and overwrites first save point when full)
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - Parameter
+
+ Save point Data
+ uint32 {4} - Entity 1
+ uint32 {4} - Action
+ uint32 {4} - Entity 2
+ uint32 {4} - function pointer to ??
+
+*/
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+struct SavePoint {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ union {
+ uint32 intValue;
+ char charValue[5];
+ } param;
+
+ SavePoint() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param.intValue = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf("{ %s - %d - %s - %s }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param.charValue);
+ }
+};
+
+class SavePoints : Common::Serializable {
+private:
+ typedef Common::Functor1<const SavePoint&, void> Callback;
+
+public:
+
+ struct SavePointData {
+ EntityIndex entity1;
+ ActionIndex action;
+ EntityIndex entity2;
+ uint32 param;
+
+ SavePointData() {
+ entity1 = kEntityPlayer;
+ action = kActionNone;
+ entity2 = kEntityPlayer;
+ param = 0;
+ }
+
+ Common::String toString() {
+ return Common::String::printf(" { %s - %d - %s - %d }", ENTITY_NAME(entity1), action, ENTITY_NAME(entity2), param);
+ }
+ };
+
+ SavePoints(LastExpressEngine *engine);
+ ~SavePoints();
+
+ // Savepoints
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0);
+ void push(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param);
+ void pushAll(EntityIndex entity, ActionIndex action, uint32 param = 0);
+ void process();
+ void reset();
+
+ // Data
+ void addData(EntityIndex entity, ActionIndex action, uint32 param);
+
+ // Callbacks
+ void setCallback(EntityIndex index, Entity::Callback *callback);
+ Callback *getCallback(EntityIndex entity) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const;
+ void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const;
+ void callAndProcess();
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &s);
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString();
+
+ uint32 count() { return _savepoints.size(); }
+
+private:
+ static const uint32 _savePointsMaxSize = 128;
+
+ LastExpressEngine *_engine;
+
+ Common::List<SavePoint> _savepoints; ///< could be a queue, but we need to be able to iterate on the items
+ Common::Array<SavePointData> _data;
+ Callback *_callbacks[40];
+
+ SavePoint pop();
+ bool updateEntityFromData(const SavePoint &point);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SAVEPOINT_H
diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp
new file mode 100644
index 0000000000..7fd0a3f0ac
--- /dev/null
+++ b/engines/lastexpress/game/scenes.cpp
@@ -0,0 +1,1195 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/scenes.h"
+
+#include "lastexpress/data/scene.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+SceneManager::SceneManager(LastExpressEngine *engine) : _engine(engine),
+ _flagNoEntity(false), _flagDrawEntities(false), _flagDrawSequences(false), _flagCoordinates(false),
+ _coords(0, 0, 480, 640), _clockHours(NULL), _clockMinutes(NULL) {
+ _sceneLoader = new SceneLoader();
+}
+
+SceneManager::~SceneManager() {
+ delete _sceneLoader;
+
+ // Clear frames
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene cache
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadSceneDataFile(ArchiveIndex archive) {
+ // Demo only has CD2TRAIN.DAT file
+ if (_engine->isDemo())
+ archive = kArchiveCd2;
+
+ switch(archive) {
+ case kArchiveCd1:
+ case kArchiveCd2:
+ case kArchiveCd3:
+ if (!_sceneLoader->load(getArchive(Common::String::printf("CD%iTRAIN.DAT", archive))))
+ error("SceneManager::loadSceneDataFile: cannot load data file CD%iTRAIN.DAT", archive);
+ break;
+
+ default:
+ case kArchiveAll:
+ error("SceneManager::loadSceneDataFile: Invalid archive index (must be [1-3], was %d", archive);
+ break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene loading
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::loadScene(SceneIndex index) {
+ getFlags()->flag_0 = false;
+ getFlags()->flag_4 = true;
+
+ if (getState()->sceneUseBackup) {
+ Scene *scene = getScenes()->get(index);
+
+ if (scene->param3 != 255) {
+ getState()->sceneUseBackup = false;
+ getState()->sceneBackup2 = kSceneNone;
+ }
+ }
+
+ // Save shouldRedraw state and redraw if necessary
+ bool shouldRedraw = getFlags()->shouldRedraw;
+ if (shouldRedraw) {
+ shouldRedraw = false;
+ // TODO check whether we need to do that here
+ askForRedraw();
+ //redrawScreen();
+ }
+
+ // Set the scene
+ setScene(index);
+
+ // TODO Events method call (might be a low level graphic that we don't need)
+
+ if (getFlags()->isGameRunning && getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = shouldRedraw;
+
+ getLogic()->updateCursor();
+}
+
+void SceneManager::loadSceneFromObject(ObjectIndex object, bool alternate) {
+ switch (object) {
+ default:
+ break;
+
+ case kObjectCompartment1:
+ case kObjectCompartment2:
+ case kObjectCompartment3:
+ case kObjectCompartment4:
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 1) * 2));
+ else
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(38 - (object - 1) * 2));
+ break;
+
+ case kObjectCompartmentA:
+ case kObjectCompartmentB:
+ case kObjectCompartmentC:
+ case kObjectCompartmentD:
+ case kObjectCompartmentE:
+ case kObjectCompartmentF:
+ case kObjectCompartmentG:
+ if (alternate)
+ loadSceneFromPosition(kCarGreenSleeping, (Position)(17 - (object - 32) * 2));
+ else
+ loadSceneFromPosition(kCarRedSleeping, (Position)(38 - (object - 32) * 2));
+ break;
+
+ case kObjectCompartment8:
+ case kObjectCompartmentH:
+ loadSceneFromPosition(object == kObjectCompartment8 ? kCarGreenSleeping : kCarRedSleeping, alternate ? 3 : 25);
+ break;
+ }
+}
+
+void SceneManager::loadSceneFromItem(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Get the scene index from the item
+ SceneIndex index = getInventory()->get(item)->scene;
+ if (!index)
+ return;
+
+ if (!getState()->sceneUseBackup) {
+ getState()->sceneUseBackup = true;
+ getState()->sceneBackup = getState()->scene;
+ }
+
+ loadScene(index);
+}
+
+void SceneManager::loadSceneFromPosition(CarIndex car, Position position, int param3) {
+ loadScene(getSceneIndexFromPosition(car, position, param3));
+}
+
+void SceneManager::loadSceneFromItemPosition(InventoryItem item) {
+ if (item >= kPortraitOriginal)
+ return;
+
+ // Check item location
+ Inventory::InventoryEntry *entry = getInventory()->get(item);
+ if (!entry->location)
+ return;
+
+ // Reset location
+ entry->location = kObjectLocationNone;
+
+ if (item != kItem3 && item != kItem5 && item != kItem7)
+ return;
+
+ // Set field value
+ CarIndex car = kCarRestaurant;
+ if (item == kItem5) car = kCarRedSleeping;
+ if (item == kItem7) car = kCarGreenSleeping;
+
+ if (!getEntities()->isInsideTrainCar(kEntityPlayer, car))
+ return;
+
+ if (getFlags()->flag_0)
+ return;
+
+ // Get current scene position
+ Scene *scene = getScenes()->get(getState()->scene);
+ Position position = scene->position;
+
+ if (getState()->sceneUseBackup) {
+ Scene *sceneBackup = getScenes()->get(getState()->sceneBackup);
+ position = sceneBackup->position;
+ }
+
+ // Checks are different for each item
+ if ((item == kItem3 && position == 56)
+ || (item == kItem5 && (position >= 23 && position <= 32))
+ || (item == kItem7 && (position == 1 || (position >= 22 && position <= 33)))) {
+ if (getState()->sceneUseBackup)
+ getState()->sceneBackup = getSceneIndexFromPosition(car, position);
+ else
+ loadSceneFromPosition(car, position);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene drawing & processing
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::setScene(SceneIndex index) {
+ _flagNoEntity = false;
+
+ if (_flagDrawEntities) {
+ // TODO Setup screen size (0, 80)x(480x480) (is it necessary for our animations?)
+ drawScene(index);
+ _flagNoEntity = true;
+ } else {
+ _flagDrawEntities = true;
+ drawScene(index);
+ _flagDrawEntities = false;
+ }
+}
+
+void SceneManager::drawScene(SceneIndex index) {
+
+ //////////////////////////////////////////////////////////////////////////
+ // Preprocess
+ preProcessScene(&index);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw background
+ debugC(9, kLastExpressDebugScenes, "== Drawing scene: %d ==", index);
+
+ // Update scene
+ _engine->getGraphicsManager()->draw(get(index), GraphicsManager::kBackgroundC, true);
+ getState()->scene = index;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update entities
+ Scene *scene = (getState()->sceneUseBackup ? get(getState()->sceneBackup) : get(index));
+
+ getEntityData(kEntityPlayer)->entityPosition = scene->entityPosition;
+ getEntityData(kEntityPlayer)->car = scene->car;
+
+ getFlags()->flag_3 = true;
+
+ if (getFlags()->isGameRunning) {
+ getSavePoints()->pushAll(kEntityPlayer, kActionDrawScene);
+ getSavePoints()->process();
+
+ if (_flagNoEntity)
+ return;
+
+ getEntities()->updateFields();
+ getEntities()->updateSequences();
+ getEntities()->updateCallbacks();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Show the scene
+ askForRedraw();
+ redrawScreen();
+
+ ////////////////////////////////////////////////////////////
+ // Post process scene
+ postProcessScene();
+}
+
+void SceneManager::processScene() {
+ if (!getState()->sceneUseBackup) {
+ loadScene(getState()->scene);
+ return;
+ }
+
+ getState()->sceneUseBackup = false;
+
+ // Select item if needed
+ InventoryItem item = getInventory()->getFirstExaminableItem();
+ if (item && getInventory()->getSelectedItem() == item)
+ getInventory()->selectItem(item);
+
+ Scene *backup = getScenes()->get(getState()->sceneBackup);
+
+ if (getEntities()->getPosition(backup->car, backup->position))
+ loadScene(processIndex(getState()->sceneBackup));
+ else
+ loadScene(getState()->sceneBackup);
+}
+
+LastExpress::SceneIndex SceneManager::processIndex(SceneIndex index) {
+ Scene *scene = get(index);
+ CarIndex car = scene->car;
+
+ switch (car) {
+ default:
+ break;
+
+ case kCarRedSleeping:
+ if (checkPosition(index, kCheckPositionLookingAtDoors)) {
+ Position position = (Position)(scene->position + (checkPosition(kSceneNone, kCheckPositionLookingUp) ? -1 : 1));
+
+ if (position == 4)
+ position = 3;
+
+ if (position == 24)
+ position = 25;
+
+ if (getEntities()->getPosition(car, position))
+ return index;
+ else
+ return getSceneIndexFromPosition(car, position);
+ } else {
+ switch (scene->position) {
+ default:
+ break;
+
+ case 41:
+ case 51:
+ if (!getEntities()->getPosition(car, 39))
+ return getSceneIndexFromPosition(car, 39);
+ // Fallback to next case
+
+ case 42:
+ case 52:
+ if (!getEntities()->getPosition(car, 14))
+ return getSceneIndexFromPosition(car, 14);
+ // Fallback to next case
+
+ case 43:
+ case 53:
+ if (!getEntities()->getPosition(car, 35))
+ return getSceneIndexFromPosition(car, 35);
+ // Fallback to next case
+
+ case 44:
+ case 54:
+ if (!getEntities()->getPosition(car, 10))
+ return getSceneIndexFromPosition(car, 10);
+ // Fallback to next case
+
+ case 45:
+ case 55:
+ if (!getEntities()->getPosition(car, 32))
+ return getSceneIndexFromPosition(car, 32);
+ // Fallback to next case
+
+ case 46:
+ case 56:
+ if (!getEntities()->getPosition(car, 7))
+ return getSceneIndexFromPosition(car, 7);
+ // Fallback to next case
+
+ case 47:
+ case 57:
+ if (!getEntities()->getPosition(car, 27))
+ return getSceneIndexFromPosition(car, 27);
+ // Fallback to next case
+
+ case 48:
+ case 58:
+ if (!getEntities()->getPosition(car, 2))
+ return getSceneIndexFromPosition(car, 2);
+ break;
+ }
+ }
+ break;
+
+ case kCarRestaurant:
+ switch (scene->position) {
+ default:
+ break;
+
+ case 52:
+ case 53:
+ case 54:
+ if (!getEntities()->getPosition(car, 51))
+ return getSceneIndexFromPosition(car, 51);
+ // Fallback to next case
+
+ case 50:
+ case 56:
+ case 57:
+ case 58:
+ if (!getEntities()->getPosition(car, 55))
+ return getSceneIndexFromPosition(car, 55);
+ // Fallback to next case
+
+ case 59:
+ if (!getEntities()->getPosition(car, 60))
+ return getSceneIndexFromPosition(car, 60);
+ // Fallback to next case
+
+ case 60:
+ if (!getEntities()->getPosition(car, 59))
+ return getSceneIndexFromPosition(car, 59);
+ // Fallback to next case
+
+ case 62:
+ case 63:
+ case 64:
+ if (!getEntities()->getPosition(car, 61))
+ return getSceneIndexFromPosition(car, 61);
+ // Fallback to next case
+
+ case 66:
+ case 67:
+ case 68:
+ if (!getEntities()->getPosition(car, 65))
+ return getSceneIndexFromPosition(car, 65);
+ // Fallback to next case
+
+ case 69:
+ case 71:
+ if (!getEntities()->getPosition(car, 70))
+ return getSceneIndexFromPosition(car, 70);
+ break;
+ }
+ break;
+ }
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Checks
+//////////////////////////////////////////////////////////////////////////
+bool SceneManager::checkPosition(SceneIndex index, CheckPositionType type) const {
+ Scene *scene = getScenes()->get((index ? index : getState()->scene));
+
+ CarIndex car = (CarIndex)scene->car;
+ Position position = scene->position;
+
+ bool isInSleepingCar = (car == kCarGreenSleeping || car == kCarRedSleeping);
+
+ switch (type) {
+ default:
+ error("SceneManager::checkPosition: Invalid position type: %d", type);
+
+ case kCheckPositionLookingUp:
+ return isInSleepingCar && (position >= 1 && position <= 19);
+
+ case kCheckPositionLookingDown:
+ return isInSleepingCar && (position >= 21 && position <= 40);
+
+ case kCheckPositionLookingAtDoors:
+ return isInSleepingCar && ((position >= 2 && position <= 17) || (position >= 23 && position <= 39));
+
+ case kCheckPositionLookingAtClock:
+ return car == kCarRestaurant && position == 81;
+ }
+}
+
+bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ Position position = scene->position;
+ CarIndex car = (CarIndex)scene->car;
+
+ if (!doCheckOtherCars)
+ return (car == kCarGreenSleeping || car == kCarRedSleeping)
+ && ((position >= 41 && position <= 48) || (position >= 51 && position <= 58));
+
+ if (position == 99)
+ return true;
+
+ switch (car){
+ default:
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ case kCarLocomotive:
+ if ((position >= 1 && position <= 18) || (position >= 22 && position <= 40))
+ return true;
+ break;
+
+ case kCarRestaurant:
+ if (position >= 73 && position <= 80)
+ return true;
+
+ if (position == 10 || position == 11)
+ return true;
+
+ break;
+
+ case kCarBaggage:
+ switch (position) {
+ default:
+ break;
+
+ case 10:
+ case 11:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ case 90:
+ case 91:
+ return true;
+ }
+ break;
+
+ case kCarCoalTender:
+ if (position == 2 || position == 10 || position == 11)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Train
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::updateDoorsAndClock() {
+ // Clear all sequences from the list
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door) {
+ removeFromQueue(*door);
+ setCoordinates(*door);
+ SAFE_DELETE(*door);
+ }
+
+ // Cleanup doors sequences
+ _doors.clear();
+
+ if (_clockHours) {
+ removeFromQueue(_clockHours);
+ setCoordinates(_clockHours);
+ SAFE_DELETE(_clockHours);
+ }
+
+ if (_clockMinutes) {
+ removeFromQueue(_clockMinutes);
+ setCoordinates(_clockMinutes);
+ SAFE_DELETE(_clockMinutes);
+ }
+
+ // Queue doors sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtDoors)) {
+
+ ObjectIndex firstIndex = kObjectNone;
+
+ // Init objectIndex (or exit if not in one of the two compartment cars
+ if (getEntityData(kEntityPlayer)->car == kCarGreenSleeping)
+ firstIndex = kObjectCompartment1;
+ else if (getEntityData(kEntityPlayer)->car == kCarRedSleeping)
+ firstIndex = kObjectCompartmentA;
+ else
+ return;
+
+ // Iterate over each door
+ for (ObjectIndex index = firstIndex; index < (ObjectIndex)(firstIndex + 8); index = (ObjectIndex)(index + 1)) {
+
+ // Doors is not open, nothing to do
+ if (getObjects()->get(index).location != kObjectLocation2)
+ continue;
+
+ // Load door sequence
+ Scene *scene = getScenes()->get(getState()->scene);
+ Common::String name = Common::String::printf("633X%c-%02d.seq", (index - firstIndex) + 65, scene->position);
+ Sequence *sequence = loadSequence1(name, 255);
+
+ // If the sequence doesn't exists, skip
+ if (!sequence || !sequence->isLoaded())
+ continue;
+
+ // Adjust frame data and store in frame list
+ SequenceFrame *frame = new SequenceFrame(sequence, 0, true);
+ frame->getInfo()->location = (checkPosition(kSceneNone, kCheckPositionLookingUp) ? (firstIndex - index) - 1 : (index - firstIndex) - 8);
+
+ _doors.push_back(frame);
+
+ // Add frame to list
+ addToQueue(frame);
+ }
+ }
+
+ // Queue clock sequences for display
+ if (checkPosition(kSceneNone, kCheckPositionLookingAtClock)) {
+ // Only used in scene 349 to show the hands on the clock
+
+ Sequence *sequenceHours = loadSequence1("SCLKH-81.seq", 255);
+ Sequence *sequenceMinutes = loadSequence1("SCLKM-81.seq", 255);
+
+ // Compute hours and minutes indexes
+ uint16 hoursIndex = (uint)getState()->time % 1296000 % 54000 / 900;
+
+ uint hours = ((uint)getState()->time % 1296000) / 54000;
+ if (hours >= 12)
+ hours -= 12;
+
+ uint16 minutesIndex = (uint16)(5 * hours + hoursIndex / 12);
+
+ // Adjust z-order and store sequences to list
+ _clockHours = new SequenceFrame(sequenceHours, hoursIndex, true);
+ _clockHours->getInfo()->location = 65534;
+
+ _clockMinutes = new SequenceFrame(sequenceMinutes, minutesIndex, true);
+ _clockMinutes->getInfo()->location = 65535;
+
+ addToQueue(_clockHours);
+ addToQueue(_clockMinutes);
+ }
+}
+
+void SceneManager::resetDoorsAndClock() {
+ for (Common::List<SequenceFrame *>::iterator door = _doors.begin(); door != _doors.end(); ++door)
+ SAFE_DELETE(*door);
+
+ _doors.clear();
+
+ SAFE_DELETE(_clockHours);
+ SAFE_DELETE(_clockMinutes);
+
+ // Remove the beetle sequences too if needed
+ getBeetle()->unload();
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sequence list
+//////////////////////////////////////////////////////////////////////////
+void SceneManager::drawFrames(bool refreshScreen) {
+ if (!_flagDrawSequences)
+ return;
+
+ // TODO handle flag coordinates
+
+ clearBg(GraphicsManager::kBackgroundOverlay);
+
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i)
+ _engine->getGraphicsManager()->draw(*i, GraphicsManager::kBackgroundOverlay);
+
+ if (refreshScreen) {
+ askForRedraw();
+ //redrawScreen();
+
+ _flagDrawSequences = false;
+ }
+}
+
+void SceneManager::addToQueue(SequenceFrame * const frame) {
+ if (!frame)
+ return;
+
+ // First check that the frame is not already in the queue
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i))
+ return;
+ }
+
+ debugC(8, kLastExpressDebugGraphics, "Adding frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Set flag
+ _flagDrawSequences = true;
+
+ // Queue empty: just insert the frame
+ if (_queue.empty()) {
+ _queue.push_back(frame);
+ return;
+ }
+
+ // Frame is closer: insert in first place
+ if (frame->getInfo()->location > _queue.front()->getInfo()->location) {
+ _queue.push_front(frame);
+ return;
+ }
+
+ // Insert the frame in the queue based on location
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->getInfo()->location > (*i)->getInfo()->location) {
+ _queue.insert(i, frame);
+ return;
+ }
+ }
+
+ // We are the last frame in location order, insert at the back of the queue
+ _queue.push_back(frame);
+}
+
+void SceneManager::removeFromQueue(SequenceFrame *frame) {
+ if (!frame)
+ return;
+
+ debugC(8, kLastExpressDebugGraphics, "Removing frame: %s / %d", frame->getName().c_str(), frame->getFrame());
+
+ // Check that the frame is in the queue and remove it
+ for (Common::List<SequenceFrame *>::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ if (frame->equal(*i)) {
+ _queue.erase(i);
+ _flagDrawSequences = true;
+ break;
+ }
+ }
+}
+
+void SceneManager::removeAndRedraw(SequenceFrame **frame, bool doRedraw) {
+ if (!frame)
+ return;
+
+ removeFromQueue(*frame);
+
+ if (doRedraw)
+ drawFrames(true);
+
+ SAFE_DELETE(*frame);
+}
+
+void SceneManager::resetQueue() {
+ _flagDrawSequences = true;
+
+ // The original engine only deletes decompressed data, not the "sequences" since they are just pointers to a memory pool
+ _queue.clear();
+}
+
+void SceneManager::setCoordinates(SequenceFrame *frame) {
+
+ if (!frame || frame->getInfo()->subType == 3)
+ return;
+
+ _flagCoordinates = true;
+
+ if (_coords.right > (int)frame->getInfo()->xPos1)
+ _coords.right = (int16)frame->getInfo()->xPos1;
+
+ if (_coords.bottom > (int)frame->getInfo()->yPos1)
+ _coords.bottom = (int16)frame->getInfo()->yPos1;
+
+ if (_coords.left < (int)frame->getInfo()->xPos2)
+ _coords.left = (int16)frame->getInfo()->xPos2;
+
+ if (_coords.top < (int)frame->getInfo()->yPos2)
+ _coords.top = (int16)frame->getInfo()->yPos2;
+}
+
+void SceneManager::resetCoordinates() {
+ _coords.top = 0;
+ _coords.left = 0;
+ _coords.bottom = 480;
+ _coords.right = 640;
+
+ _flagCoordinates = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////////
+SceneIndex SceneManager::getSceneIndexFromPosition(CarIndex car, Position position, int param3) {
+ // Probably can't happen (can we be called during cd-swap?)
+ if (_sceneLoader->count() <= 1)
+ return getState()->scene;
+
+ SceneIndex index = kSceneMenu;
+
+ Scene *firstScene = getScenes()->get(index);
+
+ while (firstScene->car != car
+ || firstScene->position != position
+ || ((param3 != -1 || firstScene->param3) && firstScene->param3 != param3 && firstScene->type != Scene::kTypeItem3)) {
+
+ // Increment index and look at the next scene
+ index = (SceneIndex)(index + 1);
+
+ if (index >= _sceneLoader->count())
+ return getState()->scene;
+
+ // Load the next scene
+ firstScene = getScenes()->get(index);
+ }
+
+ // Process index if necessary
+ Scene *scene = getScenes()->get(index);
+ if (getEntities()->getPosition(scene->car, scene->position))
+ return processIndex(index);
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Scene processing
+//////////////////////////////////////////////////////////////////////////
+
+// Process hotspots
+// - if it returns kSceneInvalid, the hotspot scene has not been modified
+// - if it returns kSceneNone, it has been modified
+//
+// Note: we use the original hotspot scene to pre-process again
+#define PROCESS_HOTSPOT_SCENE(hotspot, index) { \
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot); \
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? (hotspot)->scene : processedScene; \
+ if (testScene) { \
+ *index = (hotspot)->scene; \
+ preProcessScene(index); \
+ } \
+}
+
+void SceneManager::preProcessScene(SceneIndex *index) {
+
+ // Check index validity
+ if (*index == 0 || *index > 2500)
+ *index = kSceneMenu;
+
+ Scene *scene = getScenes()->get(*index);
+
+ switch (scene->type) {
+ case Scene::kTypeObject: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (getObjects()->get(object).location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem: {
+ InventoryItem item = (InventoryItem)scene->param1;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem2: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectItem: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+ InventoryItem item = (InventoryItem)scene->param2;
+
+ if (object >= kObjectMax)
+ break;
+
+ if (item >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getObjects()->get(object).location == kObjectLocation2)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getObjects()->get(object).location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item)->location != (*it)->param2)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeItem3: {
+ InventoryItem item1 = (InventoryItem)scene->param1;
+ InventoryItem item2 = (InventoryItem)scene->param2;
+ InventoryItem item3 = (InventoryItem)scene->param3;
+
+ if (item1 >= kPortraitOriginal || item2 >= kPortraitOriginal || item3 >= kPortraitOriginal)
+ break;
+
+ int location = kObjectLocationNone;
+
+ if (getInventory()->get(item1)->location != kObjectLocationNone)
+ location = kObjectLocation1;
+
+ if (getInventory()->get(item2)->location != kObjectLocationNone)
+ location |= kObjectLocation2;
+
+ if (getInventory()->get(item3)->location != kObjectLocationNone)
+ location |= kObjectLocation4;
+
+ if (!location)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (location != (*it)->location)
+ continue;
+
+ if (getInventory()->get(item1)->location != (*it)->param1)
+ continue;
+
+ if (getInventory()->get(item2)->location != (*it)->param2)
+ continue;
+
+ if (getInventory()->get(item3)->location != (*it)->param3)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ break;
+ }
+
+ case Scene::kTypeObjectLocation2: {
+ ObjectIndex object = (ObjectIndex)scene->param1;
+
+ if (object >= kObjectMax)
+ break;
+
+ bool found = false;
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getObjects()->get(object).location2 != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ found = true;
+ break;
+ }
+
+ // If we haven't found a proper hotspot, use the first hotspot from the current scene
+ if (!found) {
+ Scene *sceneHotspot = getScenes()->get(*index);
+ SceneHotspot *hotspot = sceneHotspot->getHotspot();
+
+ PROCESS_HOTSPOT_SCENE(hotspot, index);
+ }
+ break;
+ }
+
+ case Scene::kTypeCompartments:
+ case Scene::kTypeCompartmentsItem:
+ if (scene->param1 >= 16)
+ break;
+
+ if (getEntities()->getCompartments(scene->param1) || getEntities()->getCompartments1(scene->param1)) {
+
+ Scene *currentScene = getScenes()->get(getState()->scene);
+
+ if ((checkPosition(getState()->scene, kCheckPositionLookingUp) && checkPosition(*index, kCheckPositionLookingUp) && currentScene->entityPosition < scene->entityPosition)
+ || (checkPosition(getState()->scene, kCheckPositionLookingDown) && checkPosition(*index, kCheckPositionLookingDown) && currentScene->entityPosition > scene->entityPosition)) {
+
+ if (State::getPowerOfTwo((uint32)getEntities()->getCompartments(scene->param1)) != 30
+ && State::getPowerOfTwo((uint32)getEntities()->getCompartments1(scene->param1)) != 30 )
+ getSound()->playSound(kEntityPlayer, "CAT1126A");
+
+ *index = scene->getHotspot()->scene;
+ } else {
+ *index = scene->getHotspot(1)->scene;
+ }
+
+ preProcessScene(index);
+ } else {
+ // Stop processing here for kTypeCompartments
+ if (scene->type == Scene::kTypeCompartments)
+ break;
+
+ InventoryItem item = (InventoryItem)scene->param2;
+ if (item >= kPortraitOriginal)
+ break;
+
+ if (getInventory()->get(item)->location == kObjectLocationNone)
+ break;
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (getInventory()->get(item)->location != (*it)->location)
+ continue;
+
+ PROCESS_HOTSPOT_SCENE(*it, index);
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Sound processing
+ Scene *newScene = getScenes()->get(*index);
+ if (getSound()->isBuffered(kEntityTables4)) {
+ if (newScene->type != Scene::kTypeReadText || newScene->param1)
+ getSound()->processEntry(kEntityTables4);
+ }
+
+ // Cleanup beetle sequences
+ if (getBeetle()->isLoaded()) {
+ if (newScene->type != Scene::kTypeLoadBeetleSequences)
+ getBeetle()->unload();
+ }
+}
+
+void SceneManager::postProcessScene() {
+
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ switch (scene->type) {
+ case Scene::kTypeList: {
+
+ // Adjust time
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)((scene->param1 + 10) * getState()->timeDelta));
+ getState()->timeTicks += (scene->param1 + 10);
+
+ // Wait for a number of frames unless right mouse is clicked
+ uint32 nextFrameCount = getFrameCount() + 4 * scene->param1;
+ if (!getFlags()->mouseRightClick) {
+ while (nextFrameCount > getFrameCount()) {
+ _engine->pollEvents();
+
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ getSound()->updateSubtitles();
+ }
+ }
+
+ // Process hotspots and load scenes in the list
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (getFlags()->mouseRightClick) {
+
+ while (getScenes()->get(testScene)->type == Scene::kTypeList) {
+ hotspot = getScenes()->get(testScene)->getHotspot();
+ processedScene = getAction()->processHotspot(*hotspot);
+ testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+ }
+ }
+
+ // If several entities are there, choose one to sound "Excuse me"
+ EntityPosition entityPosition = getEntityData(kEntityPlayer)->entityPosition;
+ if (getEntityData(kEntityPlayer)->car == kCar9 && (entityPosition == kPosition_4 || entityPosition == kPosition_3)) {
+ EntityIndex entities[39];
+
+ // Init entities
+ entities[0] = kEntityPlayer;
+
+ uint progress = 0;
+
+ for (uint i = 1; i < 40 /* number of entities */; i++) {
+ CarIndex car = getEntityData((EntityIndex)i)->car;
+ EntityPosition position = getEntityData((EntityIndex)i)->entityPosition;
+
+ if (entityPosition == kPosition_4) {
+ if ((car == kCarRedSleeping && position > kPosition_9270) || (car == kCarRestaurant && position < kPosition_1540))
+ entities[progress++] = (EntityIndex)i;
+ } else {
+ if ((car == kCarGreenSleeping && position > kPosition_9270) || (car == kCarRedSleeping && position < kPosition_850))
+ entities[progress++] = (EntityIndex)i;
+ }
+ }
+
+ if (progress)
+ getSound()->excuseMe((progress == 1) ? entities[0] : entities[rnd(progress)], kEntityPlayer, SoundManager::kFlagDefault);
+ }
+
+ if (hotspot->scene)
+ setScene(hotspot->scene);
+ break;
+ }
+
+ case Scene::kTypeSavePointChapter:
+ if (getProgress().field_18 == 2)
+ getSavePoints()->push(kEntityPlayer, kEntityChapters, kActionEndChapter);
+ break;
+
+ case Scene::kTypeLoadBeetleSequences:
+ if ((getProgress().chapter == kChapter2 || getProgress().chapter == kChapter3)
+ && getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
+ if (!getBeetle()->isLoaded())
+ getBeetle()->load();
+ }
+ break;
+
+ case Scene::kTypeGameOver:
+ if (getState()->time >= kTimeCityGalanta || getProgress().field_18 == 4)
+ break;
+
+ getSound()->processEntry(SoundManager::kSoundType7);
+ getSound()->playSound(kEntityTrain, "LIB050", SoundManager::kFlagDefault);
+
+ switch (getProgress().chapter) {
+ default:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverPolice2, true);
+ break;
+
+ case kChapter1:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm, true);
+ break;
+
+ case kChapter4:
+ getLogic()->gameOver(kSavegameTypeIndex, 0, kSceneGameOverAlarm2, true);
+ break;
+ }
+ break;
+
+ case Scene::kTypeReadText:
+ getSound()->readText(scene->param1);
+ break;
+
+ case Scene::kType133:
+ if (getFlags()->flag_0) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ getLogic()->updateCursor();
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h
new file mode 100644
index 0000000000..b70526839f
--- /dev/null
+++ b/engines/lastexpress/game/scenes.h
@@ -0,0 +1,120 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef LASTEXPRESS_SCENEMANAGER_H
+#define LASTEXPRESS_SCENEMANAGER_H
+
+#include "lastexpress/data/scene.h"
+
+#include "common/hashmap.h"
+#include "common/list.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class SceneLoader;
+class Sequence;
+class SequenceFrame;
+
+class SceneManager {
+public:
+ enum CheckPositionType {
+ kCheckPositionLookingUp,
+ kCheckPositionLookingDown,
+ kCheckPositionLookingAtDoors,
+ kCheckPositionLookingAtClock
+ };
+
+ SceneManager(LastExpressEngine *engine);
+ ~SceneManager();
+
+ // Scene cache
+ void loadSceneDataFile(ArchiveIndex archive);
+ Scene *get(SceneIndex sceneIndex) { return _sceneLoader->get(sceneIndex); }
+
+ // Scene loading
+ void setScene(SceneIndex sceneIndex);
+ void loadScene(SceneIndex sceneIndex);
+ void loadSceneFromObject(ObjectIndex object, bool alternate = false);
+ void loadSceneFromItem(InventoryItem item);
+ void loadSceneFromItemPosition(InventoryItem item);
+ void loadSceneFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ // Scene drawing & processing
+ void drawScene(SceneIndex sceneIndex);
+ void processScene();
+ SceneIndex processIndex(SceneIndex sceneIndex);
+
+ // Checks
+ bool checkPosition(SceneIndex sceneIndex, CheckPositionType type) const;
+ bool checkCurrentPosition(bool doCheckOtherCars) const;
+
+ // Train
+ void updateDoorsAndClock();
+ void resetDoorsAndClock();
+
+ // Sequence queue
+ void drawFrames(bool refreshScreen);
+ void addToQueue(SequenceFrame * const frame);
+ void removeFromQueue(SequenceFrame *frame);
+ void removeAndRedraw(SequenceFrame **frame, bool doRedraw);
+ void resetQueue();
+ void setCoordinates(SequenceFrame *frame);
+
+ // Helpers
+ SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1);
+
+ void setFlagDrawSequences() { _flagDrawSequences = true; }
+
+private:
+ LastExpressEngine *_engine;
+ SceneLoader *_sceneLoader; ///< Scene loader
+
+ // Flags
+ bool _flagNoEntity;
+ bool _flagDrawEntities;
+ bool _flagDrawSequences;
+ bool _flagCoordinates;
+
+ Common::Rect _coords;
+
+ // Train sequences
+ Common::List<SequenceFrame *> _doors;
+ SequenceFrame *_clockHours;
+ SequenceFrame *_clockMinutes;
+
+ // Sequence queue
+ Common::List<SequenceFrame *> _queue;
+
+ // Scene processing
+ void preProcessScene(SceneIndex *index);
+ void postProcessScene();
+
+ void resetCoordinates();
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SCENEMANAGER_H
diff --git a/engines/lastexpress/game/sound.cpp b/engines/lastexpress/game/sound.cpp
new file mode 100644
index 0000000000..0dfc38b1b0
--- /dev/null
+++ b/engines/lastexpress/game/sound.cpp
@@ -0,0 +1,1734 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/data/snd.h"
+#include "lastexpress/data/subtitle.h"
+
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+// Letters & messages
+const char *messages[24] = {
+ "",
+ "TXT1001", // 1
+ "TXT1001A", // 2
+ "TXT1011", // 3
+ "TXT1012", // 4
+ "TXT1013", // 5
+ "TXT1014", // 6
+ "TXT1020", // 7
+ "TXT1030", // 8
+ "END1009B", // 50
+ "END1046", // 51
+ "END1047", // 52
+ "END1112", // 53
+ "END1112A", // 54
+ "END1503", // 55
+ "END1505A", // 56
+ "END1505B", // 57
+ "END1610", // 58
+ "END1612A", // 59
+ "END1612C", // 61
+ "END1612D", // 62
+ "ENDALRM1", // 63
+ "ENDALRM2", // 64
+ "ENDALRM3" // 65
+};
+
+const char *cities[17] = {
+ "EPERNAY",
+ "CHALONS",
+ "BARLEDUC",
+ "NANCY",
+ "LUNEVILL",
+ "AVRICOUR",
+ "DEUTSCHA",
+ "STRASBOU",
+ "BADENOOS",
+ "SALZBURG",
+ "ATTNANG",
+ "WELS",
+ "LINZ",
+ "VIENNA",
+ "POZSONY",
+ "GALANTA",
+ "POLICE"
+};
+
+const char *locomotiveSounds[5] = {
+ "ZFX1005",
+ "ZFX1006",
+ "ZFX1007",
+ "ZFX1007A",
+ "ZFX1007B"
+};
+
+static const SoundManager::FlagType soundFlags[32] = {
+ SoundManager::kFlagDefault, SoundManager::kFlag15, SoundManager::kFlag14, SoundManager::kFlag13, SoundManager::kFlag12,
+ SoundManager::kFlag11, SoundManager::kFlag11, SoundManager::kFlag10, SoundManager::kFlag10, SoundManager::kFlag9, SoundManager::kFlag9, SoundManager::kFlag8, SoundManager::kFlag8,
+ SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag7, SoundManager::kFlag6, SoundManager::kFlag6, SoundManager::kFlag6,
+ SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag5, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4, SoundManager::kFlag4,
+ SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3, SoundManager::kFlag3
+};
+
+SoundManager::SoundManager(LastExpressEngine *engine) : _engine(engine), _state(0), _currentType(kSoundType16), _flag(0) {
+ _soundStream = new StreamedSound();
+
+ // Initialize unknown data
+ _data0 = 0;
+ _data1 = 0;
+ _data2 = 0;
+
+ memset(&_buffer, 0, sizeof(_buffer));
+ memset(&_lastWarning, 0, sizeof(_lastWarning));
+}
+
+SoundManager::~SoundManager() {
+ delete _soundStream;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Timer
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::handleTimer() {
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+ if (entry->stream == NULL) {
+ i = _cache.reverse_erase(i);
+ continue;
+ } else if (!entry->isStreamed) {
+ entry->isStreamed = true;
+ _soundStream->load(entry->stream);
+ }
+ }
+
+ // TODO: stream any sound in the queue after filtering
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound queue management
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateQueue() {
+ //warning("Sound::unknownFunction1: not implemented!");
+}
+
+void SoundManager::resetQueue(SoundType type1, SoundType type2) {
+ if (!type2)
+ type2 = type1;
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type != type1 && (*i)->type != type2)
+ resetEntry(*i);
+ }
+}
+
+void SoundManager::removeFromQueue(EntityIndex entity) {
+ SoundEntry *entry = getEntry(entity);
+
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::removeFromQueue(Common::String filename) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (entry)
+ resetEntry(entry);
+}
+
+void SoundManager::clearQueue() {
+ _flag |= 4;
+
+ // Wait a while for a flag to be set
+ for (int i = 0; i < 3000000; i++)
+ if (_flag & 8)
+ break;
+
+ _flag |= 8;
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = (*i);
+
+ // Delete entry
+ removeEntry(entry);
+ delete entry;
+
+ i = _cache.reverse_erase(i);
+ }
+
+ updateSubtitles();
+}
+
+bool SoundManager::isBuffered(EntityIndex entity) {
+ return (getEntry(entity) != NULL);
+}
+
+bool SoundManager::isBuffered(Common::String filename, bool testForEntity) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (testForEntity)
+ return entry != NULL && !entry->entity;
+
+ return (entry != NULL);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4) {
+ if (!entry)
+ error("SoundManager::setupEntry: Invalid entry!");
+
+ entry->field_4C = a4;
+ setEntryType(entry, flag);
+ setEntryStatus(entry, flag);
+
+ // Add entry to cache
+ _cache.push_back(entry);
+
+ setupCache(entry);
+ loadSoundData(entry, name);
+}
+
+void SoundManager::setEntryType(SoundEntry *entry, FlagType flag) {
+ switch (flag & kFlagType7) {
+ default:
+ case kFlagNone:
+ entry->type = _currentType;
+ _currentType = (SoundType)(_currentType + 1);
+ break;
+
+ case kFlagType1_2: {
+ SoundEntry *previous2 = getEntry(kSoundType2);
+ if (previous2)
+ updateEntry(previous2, 0);
+
+ SoundEntry *previous = getEntry(kSoundType1);
+ if (previous) {
+ previous->type = kSoundType2;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType1;
+ }
+ break;
+
+ case kFlagType3: {
+ SoundEntry *previous = getEntry(kSoundType3);
+ if (previous) {
+ previous->type = kSoundType4;
+ updateEntry(previous, 0);
+ }
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType7: {
+ SoundEntry *previous = getEntry(kSoundType7);
+ if (previous)
+ previous->type = kSoundType8;
+
+ entry->type = kSoundType7;
+ }
+ break;
+
+ case kFlagType9: {
+ SoundEntry *previous = getEntry(kSoundType9);
+ if (previous)
+ previous->type = kSoundType10;
+
+ entry->type = kSoundType9;
+ }
+ break;
+
+ case kFlagType11: {
+ SoundEntry *previous = getEntry(kSoundType11);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType11;
+ }
+ break;
+
+ case kFlagType13: {
+ SoundEntry *previous = getEntry(kSoundType13);
+ if (previous)
+ previous->type = kSoundType14;
+
+ entry->type = kSoundType13;
+ }
+ break;
+ }
+}
+
+void SoundManager::setEntryStatus(SoundEntry *entry, FlagType flag) const {
+ SoundStatus status = (SoundStatus)flag;
+ if (!((status & 0xFF) & kSoundStatusClear1))
+ status = (SoundStatus)(status | kSoundStatusClear2);
+
+ if (((status & 0xFF00) >> 8) & kSoundStatusClear0)
+ entry->status.status = (uint32)status;
+ else
+ entry->status.status = (status | kSoundStatusClear4);
+}
+
+bool SoundManager::setupCache(SoundEntry *entry) {
+ warning("Sound::setupCache: not implemented!");
+ return true;
+}
+
+void SoundManager::clearStatus() {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ (*i)->status.status |= kSoundStatusClear3;
+}
+
+void SoundManager::loadSoundData(SoundEntry *entry, Common::String name) {
+ entry->name2 = name;
+
+ // Load sound data
+ entry->stream = getArchive(name);
+
+ if (!entry->stream)
+ entry->stream = getArchive("DEFAULT.SND");
+
+ if (entry->stream) {
+ warning("Sound::loadSoundData: not implemented!");
+ } else {
+ entry->status.status = kSoundStatusRemoved;
+ }
+}
+
+void SoundManager::resetEntry(SoundEntry *entry) const {
+ entry->status.status |= kSoundStatusRemoved;
+ entry->entity = kEntityPlayer;
+
+ if (entry->stream) {
+ if (!entry->isStreamed)
+ SAFE_DELETE(entry->stream);
+
+ entry->stream = NULL;
+ }
+}
+
+
+void SoundManager::removeEntry(SoundEntry *entry) {
+ entry->status.status |= kSoundStatusRemoved;
+
+ // Loop until ready
+ while (!(entry->status.status1 & 4) && !(_flag & 8) && (_flag & 1))
+ ; // empty loop body
+
+ // The original game remove the entry from the cache here,
+ // but since we are called from within an iterator loop
+ // we will remove the entry there
+ // removeFromCache(entry);
+
+ if (entry->subtitle) {
+ drawSubtitles(entry->subtitle);
+ SAFE_DELETE(entry->subtitle);
+ }
+
+ if (entry->entity) {
+ if (entry->entity == kEntitySteam)
+ playLoopingSound();
+ else if (entry->entity != kEntityTrain)
+ getSavePoints()->push(kEntityPlayer, entry->entity, kActionEndSound);
+ }
+}
+
+void SoundManager::updateEntry(SoundEntry *entry, uint value) const {
+ if (!(entry->status.status3 & 64)) {
+
+ int value2 = value;
+
+ entry->status.status |= kSoundStatus_100000;
+
+ if (value) {
+ if (_flag & 32) {
+ entry->field_40 = value;
+ value2 = value * 2 + 1;
+ }
+
+ entry->field_3C = value2;
+ } else {
+ entry->field_3C = 0;
+ entry->status.status |= kSoundStatus_40000000;
+ }
+ }
+}
+
+void SoundManager::updateEntryState(SoundEntry *entry) const {
+ if (_flag & 32) {
+ if (entry->type != kSoundType9 && entry->type != kSoundType7 && entry->type != kSoundType5) {
+ uint32 status = entry->status.status & kSoundStatusClear1;
+
+ entry->status.status &= kSoundStatusClearAll;
+
+ entry->field_40 = status;
+ entry->status.status |= status * 2 + 1;
+ }
+ }
+
+ entry->status.status |= kSoundStatus_20;
+}
+
+void SoundManager::processEntry(EntityIndex entity) {
+ SoundEntry *entry = getEntry(entity);
+
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntry(SoundType type) {
+ SoundEntry *entry = getEntry(type);
+
+ if (entry)
+ updateEntry(entry, 0);
+}
+
+void SoundManager::setupEntry(SoundType type, EntityIndex index) {
+ SoundEntry *entry = getEntry(type);
+
+ if (entry)
+ entry->entity = index;
+}
+
+void SoundManager::processEntry(Common::String filename) {
+ SoundEntry *entry = getEntry(filename);
+
+ if (entry) {
+ updateEntry(entry, 0);
+ entry->entity = kEntityPlayer;
+ }
+}
+
+void SoundManager::processEntries() {
+ _state = 0;
+
+ processEntry(kSoundType1);
+ processEntry(kSoundType2);
+}
+
+uint32 SoundManager::getEntryTime(EntityIndex index) {
+ SoundEntry *entry = getEntry(index);
+
+ if (!entry)
+ return 0;
+
+ return entry->time;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+
+void SoundManager::unknownFunction4() {
+ warning("Sound::unknownFunction4: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Entry search
+//////////////////////////////////////////////////////////////////////////
+SoundManager::SoundEntry *SoundManager::getEntry(EntityIndex index) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->entity == index)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(Common::String name) {
+ if (!name.contains('.'))
+ name += ".SND";
+
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->name2 == name)
+ return *i;
+ }
+
+ return NULL;
+}
+
+SoundManager::SoundEntry *SoundManager::getEntry(SoundType type) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ if ((*i)->type == type)
+ return *i;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(_state);
+ s.syncAsUint32LE(_currentType);
+
+ // Compute the number of entries to save
+ uint32 numEntries = count();
+ s.syncAsUint32LE(numEntries);
+
+ // Save or load each entry data
+ if (s.isSaving()) {
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i) {
+ SoundEntry *entry = *i;
+ if (entry->name2.matchString("NISSND?") && (entry->status.status & kFlagType7) != kFlag3) {
+ s.syncAsUint32LE(entry->status.status); // status;
+ s.syncAsUint32LE(entry->type); // type;
+ s.syncAsUint32LE(entry->field_1C); // field_8;
+ s.syncAsUint32LE(entry->time); // time;
+ s.syncAsUint32LE(entry->field_34); // field_10;
+ s.syncAsUint32LE(entry->field_38); // field_14;
+ s.syncAsUint32LE(entry->entity); // entity;
+
+ uint32 field_1C = (uint32)entry->field_48 - _data2;
+ if (field_1C > kFlag8)
+ field_1C = 0;
+ s.syncAsUint32LE(field_1C); // field_1C;
+
+ s.syncAsUint32LE(entry->field_4C); // field_20;
+
+ char name1[16];
+ strcpy((char *)&name1, entry->name1.c_str());
+ s.syncBytes((byte *)&name1, 16);
+
+ char name2[16];
+ strcpy((char *)&name2, entry->name2.c_str());
+ s.syncBytes((byte *)&name2, 16);
+ }
+ }
+ } else {
+ warning("Sound::saveLoadWithSerializer: not implemented!");
+ s.skip(numEntries * 64);
+ }
+}
+
+uint32 SoundManager::count() {
+ uint32 numEntries = 0;
+ for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
+ if ((*i)->name2.matchString("NISSND?"))
+ ++numEntries;
+
+ return numEntries;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game-related functions
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playSound(EntityIndex entity, Common::String filename, FlagType flag, byte a4) {
+ if (isBuffered(entity) && entity)
+ removeFromQueue(entity);
+
+ FlagType currentFlag = (flag == -1) ? getSoundFlag(entity) : (FlagType)(flag | 0x80000);
+
+ // Add .SND at the end of the filename if needed
+ if (!filename.contains('.'))
+ filename += ".SND";
+
+ if (!playSoundWithSubtitles(filename, currentFlag, entity, a4))
+ if (entity)
+ getSavePoints()->push(kEntityPlayer, entity, kActionEndSound);
+}
+
+SoundManager::SoundType SoundManager::playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4) {
+ SoundEntry *entry = new SoundEntry();
+ setupEntry(entry, filename, flag, 30);
+ entry->entity = entity;
+
+ if (a4) {
+ entry->field_48 = _data2 + 2 * a4;
+ entry->status.status |= kSoundStatus_8000;
+ } else {
+ // Get subtitles name
+ while (filename.size() > 4)
+ filename.deleteLastChar();
+
+ showSubtitles(entry, filename);
+ updateEntryState(entry);
+ }
+
+ return entry->type;
+}
+
+void SoundManager::playSoundEvent(EntityIndex entity, byte action, byte a3) {
+ char filename[12];
+ int values[5];
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return;
+
+ if (getEntities()->isInSalon(entity) != getEntities()->isInSalon(kEntityPlayer))
+ return;
+
+ int _action = (int)action;
+ FlagType flag = getSoundFlag(entity);
+
+ switch (action) {
+ case 36: {
+ int _param3 = (flag <= 9) ? flag + 7 : 16;
+
+ if (_param3 > 7) {
+ _data0 = (uint)_param3;
+ _data1 = _data2 + 2 * a3;
+ }
+ break;
+ }
+
+ case 37:
+ _data0 = 7;
+ _data1 = _data2 + 2 * a3;
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ _action += 1 + (int)rnd(5);
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ _action += 1 + (int)rnd(3);
+ break;
+
+ case 180:
+ _action += 1 + (int)rnd(4);
+ break;
+
+ case 246:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 105;
+ values[3] = 106;
+ values[4] = 116;
+ _action = values[rnd(5)];
+ break;
+
+ case 247:
+ values[0] = 11;
+ values[1] = 123;
+ values[2] = 124;
+ _action = values[rnd(3)];
+ break;
+
+ case 248:
+ values[0] = 0;
+ values[1] = 103;
+ values[2] = 108;
+ values[3] = 109;
+ _action = values[rnd(4)];
+ break;
+
+ case 249:
+ values[0] = 0;
+ values[1] = 56;
+ values[2] = 112;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 250:
+ values[0] = 0;
+ values[1] = 107;
+ values[2] = 115;
+ values[3] = 117;
+ _action = values[rnd(4)];
+ break;
+
+ case 251:
+ values[0] = 0;
+ values[1] = 11;
+ values[2] = 56;
+ values[3] = 113;
+ _action = values[rnd(4)];
+ break;
+
+ case 252:
+ values[0] = 0;
+ values[1] = 6;
+ values[2] = 109;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 254:
+ values[0] = 0;
+ values[1] = 104;
+ values[2] = 120;
+ values[3] = 121;
+ _action = values[rnd(4)];
+ break;
+
+ case 255:
+ values[0] = 0;
+ values[1] = 106;
+ values[2] = 115;
+ _action = values[rnd(3)];
+ break;
+
+ default:
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+
+ if (flag)
+ playSoundWithSubtitles((char*)&filename, flag, kEntityPlayer, a3);
+ }
+}
+
+void SoundManager::playSteam(CityIndex index) {
+ if (index >= ARRAYSIZE(cities))
+ error("SoundManager::playSteam: invalid city index (was %d, max %d)", index, ARRAYSIZE(cities));
+
+ _state |= kSoundState2;
+
+ if (!getEntry(kSoundType1))
+ playSoundWithSubtitles("STEAM.SND", kFlagSteam, kEntitySteam);
+
+ // Get the new sound entry and show subtitles
+ SoundEntry *entry = getEntry(kSoundType1);
+ if (entry)
+ showSubtitles(entry, cities[index]);
+}
+
+void SoundManager::playFightSound(byte action, byte a4) {
+ int _action = (int)action;
+ char filename[12];
+ int values[5];
+
+ switch (action) {
+ default:
+ break;
+
+ case 174:
+ case 184:
+ case 194:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ _action = values[rnd(3)];
+ break;
+
+ case 180:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ _action = values[rnd(4)];
+ break;
+
+ case 150:
+ case 156:
+ case 162:
+ case 168:
+ case 188:
+ case 198:
+ values[0] = action + 1;
+ values[1] = action + 2;
+ values[2] = action + 3;
+ values[3] = action + 4;
+ values[4] = action + 5;
+ _action = values[rnd(5)];
+ break;
+ }
+
+ if (_action) {
+ sprintf((char *)&filename, "LIB%03d.SND", _action);
+ playSound(kEntityTrain, (char*)&filename, kFlagDefault, a4);
+ }
+}
+
+void SoundManager::playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4) {
+ if (isBuffered(getDialogName(entityDialog)))
+ removeFromQueue(getDialogName(entityDialog));
+
+ playSound(entity, getDialogName(entityDialog), flag, a4);
+}
+
+void SoundManager::playLocomotiveSound() {
+ playSound(kEntityPlayer, locomotiveSounds[rnd(5)], (FlagType)(rnd(15) + 2));
+}
+
+const char *SoundManager::getDialogName(EntityIndex entity) const {
+ switch (entity) {
+ case kEntityAnna:
+ if (getEvent(kEventAnnaDialogGoToJerusalem))
+ return "XANN12";
+
+ if (getEvent(kEventLocomotiveRestartTrain))
+ return "XANN11";
+
+ if (getEvent(kEventAnnaBaggageTies) || getEvent(kEventAnnaBaggageTies2) || getEvent(kEventAnnaBaggageTies3) || getEvent(kEventAnnaBaggageTies4))
+ return "XANN10";
+
+ if (getEvent(kEventAnnaTired) || getEvent(kEventAnnaTiredKiss))
+ return "XANN9";
+
+ if (getEvent(kEventAnnaBaggageArgument))
+ return "XANN8";
+
+ if (getEvent(kEventKronosVisit))
+ return "XANN7";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XANN6A";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XANN6";
+
+ if (getEvent(kEventAugustPresentAnna) || getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XANN5";
+
+ if (getProgress().field_60)
+ return "XANN4";
+
+ if (getEvent(kEventAnnaGiveScarf) || getEvent(kEventAnnaGiveScarfDiner) || getEvent(kEventAnnaGiveScarfSalon)
+ || getEvent(kEventAnnaGiveScarfMonogram) || getEvent(kEventAnnaGiveScarfDinerMonogram) || getEvent(kEventAnnaGiveScarfSalonMonogram))
+ return "XANN3";
+
+ if (getEvent(kEventDinerMindJoin))
+ return "XANN2";
+
+ if (getEvent(kEventGotALight) || getEvent(kEventGotALightD))
+ return "XANN1";
+
+ break;
+
+ case kEntityAugust:
+ if (getEvent(kEventAugustTalkCigar))
+ return "XAUG6";
+
+ if (getEvent(kEventAugustBringBriefcase))
+ return "XAUG5";
+
+ // Getting closer to Vienna...
+ if (getState()->time > kTime2200500 && !getEvent(kEventAugustMerchandise))
+ return "XAUG4A";
+
+ if (getEvent(kEventAugustMerchandise))
+ return "XAUG4";
+
+ if (getEvent(kEventDinerAugust) || getEvent(kEventDinerAugustAlexeiBackground) || getEvent(kEventMeetAugustTylerCompartment)
+ || getEvent(kEventMeetAugustTylerCompartmentBed) || getEvent(kEventMeetAugustHisCompartment) || getEvent(kEventMeetAugustHisCompartmentBed))
+ return "XAUG3";
+
+ if (getEvent(kEventAugustPresentAnnaFirstIntroduction))
+ return "XAUG2";
+
+ if (getProgress().eventMertensAugustWaiting)
+ return "XAUG1";
+
+ break;
+
+ case kEntityTatiana:
+ if (getEvent(kEventTatianaTylerCompartment))
+ return "XTAT6";
+
+ if (getEvent(kEventTatianaCompartmentStealEgg))
+ return "XTAT5";
+
+ if (getEvent(kEventTatianaGivePoem))
+ return "XTAT3";
+
+ if (getProgress().field_64)
+ return "XTAT1";
+
+ break;
+
+ case kEntityVassili:
+ if (getEvent(kEventCathFreePassengers))
+ return "XVAS4";
+
+ if (getEvent(kEventVassiliCompartmentStealEgg))
+ return "XVAS3";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XVAS2";
+
+ if (getEvent(kEventVassiliSeizure))
+ return "XVAS1A";
+
+ if (getProgress().field_64)
+ return "XVAS1";
+
+ break;
+
+ case kEntityAlexei:
+ if (getProgress().field_88)
+ return "XALX6";
+
+ if (getProgress().field_8C)
+ return "XALX5";
+
+ if (getProgress().field_90)
+ return "XALX4A";
+
+ if (getProgress().field_68)
+ return "XALX4";
+
+ if (getEvent(kEventAlexeiSalonPoem))
+ return "XALX3";
+
+ if (getEvent(kEventAlexeiSalonVassili))
+ return "XALX2";
+
+ if (getEvent(kEventAlexeiDiner) || getEvent(kEventAlexeiDinerOriginalJacket))
+ return "XALX1";
+
+ break;
+
+ case kEntityAbbot:
+ if (getEvent(kEventAbbotDrinkDefuse))
+ return "XABB4";
+
+ if (getEvent(kEventAbbotInvitationDrink) || getEvent(kEventDefuseBomb))
+ return "XABB3";
+
+ if (getEvent(kEventAbbotWrongCompartment) || getEvent(kEventAbbotWrongCompartmentBed))
+ return "XABB2";
+
+ if (getEvent(kEventAbbotIntroduction))
+ return "XABB1";
+
+ break;
+
+ case kEntityMilos:
+ if (getEvent(kEventLocomotiveMilosDay) || getEvent(kEventLocomotiveMilosNight))
+ return "XMIL5";
+
+ if (getEvent(kEventMilosCompartmentVisitTyler) && (getProgress().chapter == kChapter3 || getProgress().chapter == kChapter4))
+ return "XMIL4";
+
+ if (getEvent(kEventMilosCorridorThanks) || getProgress().chapter == kChapter5)
+ return "XMIL3";
+
+ if (getEvent(kEventMilosCompartmentVisitAugust))
+ return "XMIL2";
+
+ if (getEvent(kEventMilosTylerCompartmentDefeat))
+ return "XMIL1";
+
+ break;
+
+ case kEntityVesna:
+ if (getProgress().field_94)
+ return "XVES2";
+
+ if (getProgress().field_98)
+ return "XVES1";
+
+ break;
+
+ case kEntityKronos:
+ if (getEvent(kEventKronosReturnBriefcase))
+ return "XKRO6";
+
+ if (getEvent(kEventKronosBringEggCeiling) || getEvent(kEventKronosBringEgg))
+ return "XKRO5";
+
+ if (getEvent(kEventKronosConversation) || getEvent(kEventKronosConversationFirebird)) {
+ ObjectLocation location = getInventory()->get(kItemFirebird)->location;
+ if (location != kObjectLocation6 && location != kObjectLocation5 && location != kObjectLocation2 && location != kObjectLocation1)
+ return "XKRO4A";
+ }
+
+ if (getEvent(kEventKronosConversationFirebird))
+ return "XKRO4";
+
+ if (getEvent(kEventKronosConversation)) {
+ if (!getEvent(kEventMilosCompartmentVisitAugust))
+ return "XKRO3";
+ else
+ return "XKRO2";
+ }
+
+ if (getProgress().eventMertensKronosInvitation)
+ return "XKRO1";
+
+ break;
+
+ case kEntityFrancois:
+ if (getProgress().field_9C)
+ return "XFRA3";
+
+ if (getProgress().field_A0
+ || getEvent(kEventFrancoisWhistle) || getEvent(kEventFrancoisWhistleD)
+ || getEvent(kEventFrancoisWhistleNight) || getEvent(kEventFrancoisWhistleNightD))
+ return "XFRA2";
+
+ if (getState()->time > kTimeParisEpernay) // Between Paris and Epernay
+ return "XFRA1";
+
+ break;
+
+ case kEntityMmeBoutarel:
+ if (getProgress().field_A4)
+ return "XMME4";
+
+ if (getProgress().field_A8)
+ return "XMME3";
+
+ if (getProgress().field_A0)
+ return "XMME2";
+
+ if (getProgress().field_AC)
+ return "XMME1";
+
+ break;
+
+ case kEntityBoutarel:
+ if (getProgress().eventMetBoutarel)
+ return "XMRB1";
+
+ break;
+
+ case kEntityRebecca:
+ if (getProgress().field_B4)
+ return "XREB1A";
+
+ if (getProgress().field_B8)
+ return "XREB1";
+
+ break;
+
+ case kEntitySophie:
+ if (getProgress().field_B0)
+ return "XSOP2";
+
+ if (getProgress().field_BC)
+ return "XSOP1B";
+
+ if (getProgress().field_B4)
+ return "XSOP1A";
+
+ if (getProgress().field_B8)
+ return "XSOP1";
+
+ break;
+
+ case kEntityMahmud:
+ if (getProgress().field_C4)
+ return "XMAH1";
+
+ break;
+
+ case kEntityYasmin:
+ if (getProgress().eventMetYasmin)
+ return "XHAR2";
+
+ break;
+
+ case kEntityHadija:
+ if (getProgress().eventMetHadija)
+ return "XHAR1";
+
+ break;
+
+ case kEntityAlouan:
+ if (getProgress().field_DC)
+ return "XHAR3";
+
+ break;
+
+ case kEntityGendarmes:
+ if (getProgress().field_E0)
+ return "XHAR4";
+
+ break;
+
+ case kEntityChapters:
+ if (getEvent(kEventCathDream) || getEvent(kEventCathWakingUp))
+ return "XTYL3";
+
+ return "XTYL1";
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Letters & Messages
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::readText(int id){
+ if (!isBuffered(kEntityTables4))
+ return;
+
+ if (id < 0 || (id > 8 && id < 50) || id > 64)
+ error("Sound::readText - attempting to use invalid id. Valid values [1;8] - [50;64], was %d", id);
+
+ // Get proper message file (names are stored in sequence in the array but id is [1;8] - [50;64])
+ const char *text = messages[id <= 8 ? id : id - 41];
+
+ // Check if file is in cache for id [1;8]
+ if (id <= 8)
+ if (isBuffered(text))
+ removeFromQueue(text);
+
+ playSound(kEntityTables4, text, kFlagDefault);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound bites
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playWarningCompartment(EntityIndex entity, ObjectIndex compartment) {
+
+#define PLAY_WARNING(index, sound1, sound2, sound3, sound4, sound5, sound6) { \
+ if (_lastWarning[index] + 450 >= getState()->timeTicks) { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound1, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound2 : sound3, kFlagDefault); \
+ } else { \
+ if (rnd(2)) \
+ playSound(kEntityMertens, sound4, kFlagDefault); \
+ else \
+ playSound(kEntityMertens, rnd(2) ? sound5 : sound6, kFlagDefault); \
+ } \
+ _lastWarning[index] = getState()->timeTicks; \
+}
+
+ if (entity != kEntityMertens && entity != kEntityCoudert)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Mertens
+ if (entity == kEntityMertens) {
+
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartment2:
+ PLAY_WARNING(0, "Con1502A", "Con1500B", "Con1500C", "Con1502", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment3:
+ PLAY_WARNING(1, "Con1501A", "Con1500B", "Con1500C", "Con1501", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment4:
+ PLAY_WARNING(2, "Con1503", "Con1500B", "Con1500C", "Con1503", "Con1500", "Con1500A");
+ break;
+
+ case kObjectCompartment5:
+ case kObjectCompartment6:
+ case kObjectCompartment7:
+ case kObjectCompartment8:
+ ++_lastWarning[3];
+
+ switch (_lastWarning[3]) {
+ default:
+ break;
+
+ case 1:
+ getSound()->playSound(kEntityMertens, "Con1503C", kFlagDefault);
+ break;
+
+ case 2:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503E" : "Con1503A", kFlagDefault);
+ break;
+
+ case 3:
+ getSound()->playSound(kEntityMertens, rnd(2) ? "Con1503B" : "Con1503D", kFlagDefault);
+ _lastWarning[3] = 0;
+ break;
+ }
+ }
+
+ return;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Coudert
+ switch (compartment) {
+ default:
+ break;
+
+ case kObjectCompartmentA:
+ if (_lastWarning[4] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1508" : "Jac1508A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentB:
+ if (_lastWarning[5] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTimeCityLinz && getState()->time < kTime2133000))
+ getSound()->playSound(kEntityCoudert, "Jac1507A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1507", kFlagDefault);
+ break;
+
+ case kObjectCompartmentC:
+ if (_lastWarning[6] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().chapter < kChapter3)
+ getSound()->playSound(kEntityCoudert, "Jac1506", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1506A" : "Jac1506B", kFlagDefault);
+ break;
+
+ case kObjectCompartmentD:
+ if (_lastWarning[7] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ getSound()->playSound(kEntityCoudert, "Jac1505", kFlagDefault);
+ break;
+
+ case kObjectCompartmentE:
+ if (_lastWarning[8] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2115000 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1504B", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityRebecca, kCarRedSleeping, kPosition_4840))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1504" : "Jac1504A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentF:
+ if (_lastWarning[9] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getProgress().field_40 || (getState()->time > kTime2083500 && getState()->time < kTime2133000)) {
+ getSound()->playSound(kEntityCoudert, "Jac1503B", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityAnna, kCarRedSleeping, kPosition_4070))
+ getSound()->playSound(kEntityCoudert, "Jac1503", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1503A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentG:
+ if (_lastWarning[10] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (rnd(2) || getEntities()->isInsideCompartment(kEntityMilos, kCarRedSleeping, kPosition_3050))
+ getSound()->playSound(kEntityCoudert, "Jac1502", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1502A", kFlagDefault);
+ break;
+
+ case kObjectCompartmentH:
+ if (_lastWarning[11] + 450 >= getState()->timeTicks) {
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ break;
+ }
+
+ if (getEntities()->isInsideCompartment(kEntityIvo, kCarRedSleeping, kPosition_2740))
+ getSound()->playSound(kEntityCoudert, rnd(2) ? "Jac1500" : "Jac1500A", kFlagDefault);
+ else
+ getSound()->playSound(kEntityCoudert, "Jac1501", kFlagDefault);
+ break;
+ }
+
+ // Update ticks (Compartments A - H are indexes 4 - 11)
+ _lastWarning[compartment - 28] = getState()->timeTicks;
+}
+
+void SoundManager::excuseMe(EntityIndex entity, EntityIndex entity2, FlagType flag) {
+ if (isBuffered(entity) && entity != kEntityPlayer && entity != kEntityChapters && entity != kEntityTrain)
+ return;
+
+ if (entity2 == kEntityFrancois || entity2 == kEntityMax)
+ return;
+
+ if (entity == kEntityFrancois && getEntityData(kEntityFrancois)->field_4A3 != 30)
+ return;
+
+ if (flag == kFlagNone)
+ flag = getSoundFlag(entity);
+
+ switch (entity) {
+ default:
+ break;
+
+ case kEntityAnna:
+ playSound(kEntityPlayer, "ANN1107A", flag);
+ break;
+
+ case kEntityAugust:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "AUG1100A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "AUG1100B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "AUG1100C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "AUG1100D", flag);
+ break;
+ }
+ break;
+
+ case kEntityMertens:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "CON1111" : "CON1111A"), flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "CON1110A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CON1110C", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CON1110", flag);
+ break;
+ }
+ } else {
+ if (isNight()) {
+ playSound(kEntityPlayer, (getProgress().field_18 == 2 ? "CON1110F" : "CON1110E"));
+ } else {
+ playSound(kEntityPlayer, "CON1110D");
+ }
+ }
+ }
+ break;
+
+ case kEntityCoudert:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "JAC1111D", flag);
+ } else {
+ if (entity2 || getProgress().jacket != kJacketGreen || !rnd(2)) {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "JAC1111", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "JAC1111A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "JAC1111B", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, "JAC1111C", flag);
+ break;
+ }
+ } else {
+ playSound(kEntityPlayer, "JAC1113B", flag);
+ }
+ }
+ break;
+
+ case kEntityPascale:
+ playSound(kEntityPlayer, (rnd(2) ? "HDE1002" : "HED1002A"), flag);
+ break;
+
+ case kEntityServers0:
+ case kEntityServers1:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002" : "WAT1003", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002A" : "WAT1003A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityServers0) ? "WAT1002B" : "WAT1003B", flag);
+ break;
+ }
+ break;
+
+ case kEntityVerges:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, (rnd(2) ? "TRA1113A" : "TRA1113B"));
+ } else {
+ playSound(kEntityPlayer, "TRA1112", flag);
+ }
+ break;
+
+ case kEntityTatiana:
+ playSound(kEntityPlayer, (rnd(2) ? "TAT1102A" : "TAT1102B"), flag);
+ break;
+
+ case kEntityAlexei:
+ playSound(kEntityPlayer, (rnd(2) ? "ALX1099C" : "ALX1099D"), flag);
+ break;
+
+ case kEntityAbbot:
+ if (Entities::isFemale(entity2)) {
+ playSound(kEntityPlayer, "ABB3002C", flag);
+ } else {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "ABB3002", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "ABB3002A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "ABB3002B", flag);
+ break;
+ }
+ }
+ break;
+
+ case kEntityVesna:
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "VES1109A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "VES1109B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "VES1109C", flag);
+ break;
+ }
+ break;
+
+ case kEntityKahina:
+ playSound(kEntityPlayer, (rnd(2) ? "KAH1001" : "KAH1001A"), flag);
+ break;
+
+ case kEntityFrancois:
+ case kEntityMmeBoutarel:
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001" : "MME1103A", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001A" : "MME1103B", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001B" : "MME1103C", flag);
+ break;
+
+ case 3:
+ playSound(kEntityPlayer, (entity == kEntityFrancois) ? "FRA1001C" : "MME1103D", flag);
+ break;
+ }
+ break;
+
+ case kEntityBoutarel:
+ playSound(kEntityPlayer, "MRB1104", flag);
+ if (flag > 2)
+ getProgress().eventMetBoutarel = true;
+ break;
+
+ case kEntityRebecca:
+ playSound(kEntityPlayer, (rnd(2) ? "REB1106" : "REB110A"), flag);
+ break;
+
+ case kEntitySophie: {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ playSound(kEntityPlayer, "SOP1105", flag);
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105C" : "SOP1105A", flag);
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, Entities::isFemale(entity2) ? "SOP1105D" : "SOP1105B", flag);
+ break;
+ }
+ break;
+ }
+
+ case kEntityMahmud:
+ playSound(kEntityPlayer, "MAH1101", flag);
+ break;
+
+ case kEntityYasmin:
+ playSound(kEntityPlayer, "HAR1002", flag);
+ if (flag > 2)
+ getProgress().eventMetYasmin = true;
+ break;
+
+ case kEntityHadija:
+ playSound(kEntityPlayer, (rnd(2) ? "HAR1001" : "HAR1001A"), flag);
+ if (flag > 2)
+ getProgress().eventMetHadija = true;
+ break;
+
+ case kEntityAlouan:
+ playSound(kEntityPlayer, "HAR1004", flag);
+ break;
+ }
+}
+
+void SoundManager::excuseMeCath() {
+ switch(rnd(3)) {
+ default:
+ playSound(kEntityPlayer, "CAT1126B");
+ break;
+
+ case 1:
+ playSound(kEntityPlayer, "CAT1126C");
+ break;
+
+ case 2:
+ playSound(kEntityPlayer, "CAT1126D");
+ break;
+ }
+}
+
+const char *SoundManager::justCheckingCath() const {
+ switch(rnd(4)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT5001";
+
+ case 1:
+ return "CAT5001A";
+
+ case 2:
+ return "CAT5001B";
+
+ case 3:
+ return "CAT5001C";
+ }
+
+ return "CAT5001";
+}
+
+const char *SoundManager::wrongDoorCath() const {
+ switch(rnd(5)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1125";
+
+ case 1:
+ return "CAT1125A";
+
+ case 2:
+ return "CAT1125B";
+
+ case 3:
+ return "CAT1125C";
+
+ case 4:
+ return "CAT1125D";
+ }
+
+ return "CAT1125";
+}
+
+const char *SoundManager::justAMinuteCath() const {
+ switch(rnd(3)) {
+ default:
+ break;
+
+ case 0:
+ return "CAT1520";
+
+ case 1:
+ return "CAT1521";
+
+ case 2:
+ return "CAT1125"; // ?? is this a bug in the original?
+ }
+
+ return "CAT1520";
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sound flags
+//////////////////////////////////////////////////////////////////////////
+SoundManager::FlagType SoundManager::getSoundFlag(EntityIndex entity) const {
+ if (entity == kEntityPlayer)
+ return kFlagDefault;
+
+ if (getEntityData(entity)->car != getEntityData(kEntityPlayer)->car)
+ return kFlagNone;
+
+ // Compute sound value
+ FlagType ret = kFlag2;
+
+ // Get default value if valid
+ int index = ABS(getEntityData(entity)->entityPosition - getEntityData(kEntityPlayer)->entityPosition) / 230;
+ if (index < 32)
+ ret = soundFlags[index];
+
+ if (getEntityData(entity)->location == kLocationOutsideTrain) {
+ if (getEntityData(entity)->car != kCarKronos
+ && !getEntities()->isOutsideAlexeiWindow()
+ && !getEntities()->isOutsideAnnaWindow())
+ return kFlagNone;
+
+ return (FlagType)(ret / 6);
+ }
+
+ switch (getEntityData(entity)->car) {
+ default:
+ break;
+
+ case kCarKronos:
+ if (getEntities()->isInKronosSalon(entity) != getEntities()->isInKronosSalon(kEntityPlayer))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarGreenSleeping:
+ case kCarRedSleeping:
+ if (getEntities()->isInGreenCarEntrance(kEntityPlayer) && !getEntities()->isInKronosSalon(entity))
+ ret = (FlagType)(ret * 2);
+
+ if (getEntityData(kEntityPlayer)->location
+ && (getEntityData(entity)->entityPosition != kPosition_1 || !getEntities()->isDistanceBetweenEntities(kEntityPlayer, entity, 400)))
+ ret = (FlagType)(ret * 2);
+ break;
+
+ case kCarRestaurant:
+ if (getEntities()->isInSalon(entity) == getEntities()->isInSalon(kEntityPlayer)
+ && (getEntities()->isInRestaurant(entity) != getEntities()->isInRestaurant(kEntityPlayer)))
+ ret = (FlagType)(ret * 2);
+ else
+ ret = (FlagType)(ret * 4);
+ break;
+ }
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Subtitles
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::updateSubtitles() {
+ //warning("SoundManager::updateSubtitles: not implemented!");
+}
+
+void SoundManager::showSubtitles(SoundEntry *entry, Common::String filename) {
+ warning("SoundManager::showSubtitles: not implemented!");
+}
+
+void SoundManager::drawSubtitles(SubtitleManager *subtitle) {
+ warning("SoundManager::drawSubtitles: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void SoundManager::playLoopingSound() {
+ warning("SoundManager::playLoopingSound: not implemented!");
+}
+
+void SoundManager::stopAllSound() const {
+ _soundStream->stop();
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/sound.h b/engines/lastexpress/game/sound.h
new file mode 100644
index 0000000000..8c5c6aea0d
--- /dev/null
+++ b/engines/lastexpress/game/sound.h
@@ -0,0 +1,343 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SOUND_H
+#define LASTEXPRESS_SOUND_H
+
+/*
+
+ Sound entry: 68 bytes (this is what appears in the savegames)
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+
+ Sound queue entry: 120 bytes
+ uint16 {2} - status
+ byte {1} - ??
+ byte {1} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - file data pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - archive structure pointer
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - ??
+ uint32 {4} - entity
+ uint32 {4} - ??
+ uint32 {4} - ??
+ char {16} - name 1
+ char {16} - name 2
+ uint32 {4} - pointer to next entry in the queue
+ uint32 {4} - subtitle data pointer
+
+*/
+
+#include "lastexpress/shared.h"
+
+#include "common/list.h"
+#include "common/system.h"
+#include "common/serializer.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+class StreamedSound;
+class SubtitleManager;
+
+class SoundManager : Common::Serializable {
+public:
+ enum SoundType {
+ kSoundTypeNone = 0,
+ kSoundType1,
+ kSoundType2,
+ kSoundType3,
+ kSoundType4,
+ kSoundType5,
+ kSoundType6,
+ kSoundType7,
+ kSoundType8,
+ kSoundType9,
+ kSoundType10,
+ kSoundType11,
+ kSoundType12,
+ kSoundType13,
+ kSoundType14,
+ kSoundType15,
+ kSoundType16
+ };
+
+ enum FlagType {
+ kFlagInvalid = -1,
+ kFlagNone = 0x0,
+ kFlag2 = 0x2,
+ kFlag3 = 0x3,
+ kFlag4 = 0x4,
+ kFlag5 = 0x5,
+ kFlag6 = 0x6,
+ kFlag7 = 0x7,
+ kFlag8 = 0x8,
+ kFlag9 = 0x9,
+ kFlag10 = 0xA,
+ kFlag11 = 0xB,
+ kFlag12 = 0xC,
+ kFlag13 = 0xD,
+ kFlag14 = 0xE,
+ kFlag15 = 0xF,
+ kFlagDefault = 0x10,
+
+ kFlagType1_2 = 0x1000000,
+ kFlagSteam = 0x1001007,
+ kFlagType13 = 0x3000000,
+ kFlagMenuClock = 0x3080010,
+ kFlagType7 = 0x4000000,
+ kFlagType11 = 0x5000000,
+ kFlagMusic = 0x5000010,
+ kFlagType3 = 0x6000000,
+ kFlagLoop = 0x6001008,
+ kFlagType9 = 0x7000000
+ };
+
+ SoundManager(LastExpressEngine *engine);
+ ~SoundManager();
+
+ // Timer
+ void handleTimer();
+
+ // State
+ void resetState() { _state |= kSoundType1; }
+
+ // Sound queue
+ void updateQueue();
+ void resetQueue(SoundType type1, SoundType type2 = kSoundTypeNone);
+ void clearQueue();
+
+ // Subtitles
+ void updateSubtitles();
+
+ // Entry
+ bool isBuffered(Common::String filename, bool testForEntity = false);
+ bool isBuffered(EntityIndex entity);
+ void setupEntry(SoundType type, EntityIndex index);
+ void processEntry(EntityIndex entity);
+ void processEntry(SoundType type);
+ void processEntry(Common::String filename);
+ void processEntries();
+ void removeFromQueue(Common::String filename);
+ void removeFromQueue(EntityIndex entity);
+ uint32 getEntryTime(EntityIndex index);
+
+ // Misc
+ void unknownFunction4();
+ void clearStatus();
+
+ // Sound playing
+ void playSound(EntityIndex entity, Common::String filename, FlagType flag = kFlagInvalid, byte a4 = 0);
+ SoundType playSoundWithSubtitles(Common::String filename, FlagType flag, EntityIndex entity, byte a4 = 0);
+ void playSoundEvent(EntityIndex entity, byte action, byte a3 = 0);
+ void playDialog(EntityIndex entity, EntityIndex entityDialog, FlagType flag, byte a4);
+ void playSteam(CityIndex index);
+ void playFightSound(byte action, byte a4);
+ void playLocomotiveSound();
+ void playWarningCompartment(EntityIndex entity, ObjectIndex compartment);
+
+ // Dialog & Letters
+ void readText(int id);
+ const char *getDialogName(EntityIndex entity) const;
+
+ // Sound bites
+ void excuseMe(EntityIndex entity, EntityIndex entity2 = kEntityPlayer, FlagType flag = kFlagNone);
+ void excuseMeCath();
+ const char *justCheckingCath() const;
+ const char *wrongDoorCath() const;
+ const char *justAMinuteCath() const;
+
+ // FLags
+ SoundManager::FlagType getSoundFlag(EntityIndex index) const;
+
+ // Debug
+ void stopAllSound() const;
+
+ // Serializable
+ void saveLoadWithSerializer(Common::Serializer &ser);
+ uint32 count();
+
+private:
+ typedef int32 *SoundBuffer;
+
+ enum SoundStatus {
+ kSoundStatus_20 = 0x20,
+ kSoundStatusRemoved = 0x200,
+
+ kSoundStatus_8000 = 0x8000,
+ kSoundStatus_100000 = 0x100000,
+ kSoundStatus_40000000 = 0x40000000,
+
+ kSoundStatusClear0 = 0x10,
+ kSoundStatusClear1 = 0x1F,
+ kSoundStatusClear2 = 0x80,
+ kSoundStatusClear3 = 0x200,
+ kSoundStatusClear4 = 0x800,
+ kSoundStatusClearAll = 0xFFFFFFE0
+ };
+
+ enum SoundState {
+ kSoundState0 = 0,
+ kSoundState1 = 1,
+ kSoundState2 = 2
+ };
+
+ union SoundStatusUnion {
+ uint32 status;
+ byte status1;
+ byte status2;
+ byte status3;
+ byte status4;
+
+ SoundStatusUnion() {
+ status = 0;
+ }
+ };
+
+ struct SoundEntry {
+ SoundStatusUnion status;
+ SoundType type; // int
+ //int field_8;
+ //int field_C;
+ //int field_10;
+ //int fileData;
+ //int field_18;
+ int field_1C;
+ uint32 time;
+ //int field_24;
+ //int field_28;
+ Common::SeekableReadStream *stream; // int
+ //int field_30;
+ int field_34;
+ int field_38;
+ int field_3C;
+ int field_40;
+ EntityIndex entity;
+ int field_48;
+ int field_4C;
+ Common::String name1; //char[16];
+ Common::String name2; //char[16];
+ //int next; // offset to the next structure in the list (not used)
+ SubtitleManager *subtitle;
+
+ bool isStreamed; // TEMPORARY
+
+ SoundEntry() {
+ status.status = 0;
+ type = kSoundTypeNone;
+
+ field_1C = 0;
+ time = 0;
+
+ stream = NULL;
+
+ field_34 = 0;
+ field_38 = 0;
+ field_3C = 0;
+ field_40 = 0;
+ entity = kEntityPlayer;
+ field_48 = 0;
+ field_4C = 0;
+
+ subtitle = NULL;
+
+ isStreamed = false;
+ }
+ };
+
+ // Engine
+ LastExpressEngine *_engine;
+
+ // State flag
+ int _state;
+ SoundType _currentType;
+
+ // Sound stream
+ StreamedSound *_soundStream;
+
+ // Unknown data
+ uint32 _data0;
+ uint32 _data1;
+ uint32 _data2;
+ uint32 _flag;
+
+ // Filters
+
+ int32 _buffer[2940]; ///< Static sound buffer
+
+ // Compartment warnings by Mertens or Coudert
+ uint32 _lastWarning[12];
+
+ // Looping sound
+ void playLoopingSound();
+
+ // Sound cache
+ Common::List<SoundEntry *> _cache;
+
+ SoundEntry *getEntry(EntityIndex index);
+ SoundEntry *getEntry(Common::String name);
+ SoundEntry *getEntry(SoundType type);
+
+ void setupEntry(SoundEntry *entry, Common::String name, FlagType flag, int a4);
+ void setEntryType(SoundEntry *entry, FlagType flag);
+ void setEntryStatus(SoundEntry *entry, FlagType flag) const;
+ bool setupCache(SoundEntry *entry);
+ void loadSoundData(SoundEntry *entry, Common::String name);
+
+ void updateEntry(SoundEntry *entry, uint value) const;
+ void updateEntryState(SoundEntry *entry) const ;
+ void resetEntry(SoundEntry *entry) const;
+ void removeEntry(SoundEntry *entry);
+
+ // Subtitles
+ void showSubtitles(SoundEntry *entry, Common::String filename);
+ void drawSubtitles(SubtitleManager *subtitle);
+
+ // Sound filter
+ void applyFilter(SoundEntry *entry, SoundBuffer buffer);
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SOUND_H
diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp
new file mode 100644
index 0000000000..9586c58ceb
--- /dev/null
+++ b/engines/lastexpress/game/state.cpp
@@ -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$
+ *
+ */
+
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savepoint.h"
+
+#include "lastexpress/lastexpress.h"
+
+namespace LastExpress {
+
+State::State(LastExpressEngine *engine) : _engine(engine), _timer(0) {
+ _inventory = new Inventory(engine);
+ _objects = new Objects(engine);
+ _savepoints = new SavePoints(engine);
+ _state = new GameState();
+ _flags = new Flags();
+}
+
+State::~State() {
+ delete _inventory;
+ delete _objects;
+ delete _savepoints;
+ delete _state;
+ delete _flags;
+
+ // Zero passed pointers
+ _engine = NULL;
+}
+
+bool State::isNightTime() const {
+ return (_state->progress.chapter == kChapter1
+ || _state->progress.chapter == kChapter4
+ || (_state->progress.chapter == kChapter5 && !_state->progress.isNightTime));
+}
+
+void State::getHourMinutes(uint32 time, uint8 *hours, uint8 *minutes) {
+ if (hours == NULL || minutes == NULL)
+ error("State::getHourMinutes: invalid parameters passed!");
+
+ *hours = (uint8)((time % 1296000) / 54000);
+ *minutes = (uint8)((time % 54000) / 900);
+}
+
+uint32 State::getPowerOfTwo(uint32 x) {
+ if (!x || (x & 1))
+ return 0;
+
+ uint32 num = 0;
+ do {
+ x >>= 1;
+ num++;
+ } while ((x & 1) == 0);
+
+ return num;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h
new file mode 100644
index 0000000000..135ee6bb4e
--- /dev/null
+++ b/engines/lastexpress/game/state.h
@@ -0,0 +1,657 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_STATE_H
+#define LASTEXPRESS_STATE_H
+
+#include "lastexpress/shared.h"
+
+#include "common/serializer.h"
+#include "common/system.h"
+
+namespace LastExpress {
+
+class LastExpressEngine;
+
+class Inventory;
+class Objects;
+class SavePoints;
+
+class State {
+public:
+ struct GameProgress : public Common::Serializable {
+ uint32 field_0;
+ JacketType jacket;
+ bool eventCorpseMovedFromFloor;
+ uint32 field_C;
+ bool eventCorpseFound;
+ uint32 field_14; ///< EntityIndex (used in Gendarmes)
+ uint32 field_18;
+ uint32 portrait;
+ bool eventCorpseThrown;
+ uint32 field_24;
+ uint32 field_28;
+ ChapterIndex chapter;
+ uint32 field_30;
+ bool eventMetAugust;
+ bool isNightTime;
+ uint32 field_3C;
+ uint32 field_40;
+ uint32 field_44;
+ uint32 field_48;
+ uint32 field_4C;
+ bool isTrainRunning;
+ uint32 field_54;
+ uint32 field_58;
+ uint32 field_5C;
+ uint32 field_60;
+ uint32 field_64;
+ uint32 field_68;
+ bool eventMertensAugustWaiting;
+ bool eventMertensKronosInvitation;
+ bool isEggOpen;
+ uint32 field_78; // time?
+ uint32 field_7C;
+ uint32 field_80;
+ uint32 field_84;
+ uint32 field_88;
+ uint32 field_8C;
+ uint32 field_90;
+ uint32 field_94;
+ uint32 field_98;
+ uint32 field_9C;
+ uint32 field_A0;
+ uint32 field_A4;
+ uint32 field_A8;
+ uint32 field_AC;
+ uint32 field_B0;
+ uint32 field_B4;
+ uint32 field_B8;
+ uint32 field_BC;
+ uint32 field_C0;
+ uint32 field_C4;
+ uint32 field_C8;
+ uint32 field_CC;
+ bool eventMetBoutarel;
+ bool eventMetHadija;
+ bool eventMetYasmin;
+ uint32 field_DC;
+ uint32 field_E0;
+ uint32 field_E4;
+ uint32 field_E8;
+ uint32 field_EC;
+ uint32 field_F0;
+ uint32 field_F4;
+ uint32 field_F8;
+ uint32 field_FC;
+ uint32 field_100;
+ uint32 field_104;
+ uint32 field_108;
+ uint32 field_10C;
+ uint32 field_110;
+ uint32 field_114;
+ uint32 field_118;
+ uint32 field_11C;
+ uint32 field_120;
+ uint32 field_124;
+ uint32 field_128;
+ uint32 field_12C;
+ uint32 field_130;
+ uint32 field_134;
+ uint32 field_138;
+ uint32 field_13C;
+ uint32 field_140;
+ uint32 field_144;
+ uint32 field_148;
+ uint32 field_14C;
+ uint32 field_150;
+ uint32 field_154;
+ uint32 field_158;
+ uint32 field_15C;
+ uint32 field_160;
+ uint32 field_164;
+ uint32 field_168;
+ uint32 field_16C;
+ uint32 field_170;
+ uint32 field_174;
+ uint32 field_178;
+ uint32 field_17C;
+ uint32 field_180;
+ uint32 field_184;
+ uint32 field_188;
+ uint32 field_18C;
+ uint32 field_190;
+ uint32 field_194;
+ uint32 field_198;
+ uint32 field_19C;
+ uint32 field_1A0;
+ uint32 field_1A4;
+ uint32 field_1A8;
+ uint32 field_1AC;
+ uint32 field_1B0;
+ uint32 field_1B4;
+ uint32 field_1B8;
+ uint32 field_1BC;
+ uint32 field_1C0;
+ uint32 field_1C4;
+ uint32 field_1C8;
+ uint32 field_1CC;
+ uint32 field_1D0;
+ uint32 field_1D4;
+ uint32 field_1D8;
+ uint32 field_1DC;
+ uint32 field_1E0;
+ uint32 field_1E4;
+ uint32 field_1E8;
+ uint32 field_1EC;
+ uint32 field_1F0;
+ uint32 field_1F4;
+ uint32 field_1F8;
+ uint32 field_1FC;
+
+ GameProgress() {
+ field_0 = 0;
+ jacket = kJacketOriginal;
+ eventCorpseMovedFromFloor = false;
+ field_C = 0;
+ eventCorpseFound = false;
+ field_14 = 0; // 5
+ field_18 = 0;
+ portrait = _defaultPortrait;
+ eventCorpseThrown = false;
+ field_24 = 0;
+ field_28 = 0; // 10
+ chapter = kChapter1;
+ field_30 = 0;
+ eventMetAugust = false;
+ isNightTime = false;
+ field_3C = 0; // 15
+ field_40 = 0;
+ field_44 = 0;
+ field_48 = 0;
+ field_4C = 0;
+ isTrainRunning = false; // 20
+ field_54 = 0;
+ field_58 = 0;
+ field_5C = 0;
+ field_60 = 0;
+ field_64 = 0; // 25
+ field_68 = 0;
+ eventMertensAugustWaiting = false;
+ eventMertensKronosInvitation = false;
+ isEggOpen = false;
+ field_78 = 0; // 30
+ field_7C = 0;
+ field_80 = 0;
+ field_84 = 0;
+ field_88 = 0;
+ field_8C = 0; // 35
+ field_90 = 0;
+ field_94 = 0;
+ field_98 = 0;
+ field_9C = 0;
+ field_A0 = 0; // 40
+ field_A4 = 0;
+ field_A8 = 0;
+ field_AC = 0;
+ field_B0 = 0;
+ field_B4 = 0; // 45
+ field_B8 = 0;
+ field_BC = 0;
+ field_C0 = 0;
+ field_C4 = 0;
+ field_C8 = 0; // 50
+ field_CC = 0;
+ eventMetBoutarel = false;
+ eventMetHadija = false;
+ eventMetYasmin = false;
+ field_DC = 0; // 55
+ field_E0 = 0;
+ field_E4 = 0;
+ field_E8 = 0;
+ field_EC = 0;
+ field_F0 = 0; // 60
+ field_F4 = 0;
+ field_F8 = 0;
+ field_FC = 0;
+ field_100 = 0;
+ field_104 = 0; // 65
+ field_108 = 0;
+ field_10C = 0;
+ field_110 = 0;
+ field_114 = 0;
+ field_118 = 0; // 70
+ field_11C = 0;
+ field_120 = 0;
+ field_124 = 0;
+ field_128 = 0;
+ field_12C = 0; // 75
+ field_130 = 0;
+ field_134 = 0;
+ field_138 = 0;
+ field_13C = 0;
+ field_140 = 0; // 80
+ field_144 = 0;
+ field_148 = 0;
+ field_14C = 0;
+ field_150 = 0;
+ field_154 = 0; // 85
+ field_158 = 0;
+ field_15C = 0;
+ field_160 = 0;
+ field_164 = 0;
+ field_168 = 0; // 90
+ field_16C = 0;
+ field_170 = 0;
+ field_174 = 0;
+ field_178 = 0;
+ field_17C = 0; // 95
+ field_180 = 0;
+ field_184 = 0;
+ field_188 = 0;
+ field_18C = 0;
+ field_190 = 0; // 100
+ field_194 = 0;
+ field_198 = 0;
+ field_19C = 0;
+ field_1A0 = 0;
+ field_1A4 = 0; // 105
+ field_1A8 = 0;
+ field_1AC = 0;
+ field_1B0 = 0;
+ field_1B4 = 0;
+ field_1B8 = 0; // 110
+ field_1BC = 0;
+ field_1C0 = 0;
+ field_1C4 = 0;
+ field_1C8 = 0;
+ field_1CC = 0; // 115
+ field_1D0 = 0;
+ field_1D4 = 0;
+ field_1D8 = 0;
+ field_1DC = 0;
+ field_1E0 = 0; // 120
+ field_1E4 = 0;
+ field_1E8 = 0;
+ field_1EC = 0;
+ field_1F0 = 0;
+ field_1F4 = 0; // 125
+ field_1F8 = 0;
+ field_1FC = 0;
+ }
+
+ /**
+ * Query if if a progress value is equal to the specified value.
+ *
+ * Note: This is necessary because we store different types in the progress structure
+ * and need to test a value based on an index in Action::getCursor()
+ *
+ * @param index Zero-based index of the progress structure entry
+ * @param val The value.
+ *
+ * @return true if equal, false if not.
+ */
+ bool isEqual(uint index, uint val) {
+ return getValueName(index) == val;
+ }
+
+ uint32 getValueName(uint index, Common::String *name = NULL) {
+ #define EXPOSE_VALUE(idx, entryName) \
+ case idx: { \
+ if (name) (*name) = "" #entryName; \
+ return (uint32)entryName; \
+ }
+
+ switch (index) {
+ default:
+ error("GameProgress::isEqual: invalid index value (was: %d, max:127)", index);
+ break;
+
+ EXPOSE_VALUE(0, field_0);
+ EXPOSE_VALUE(1, jacket);
+ EXPOSE_VALUE(2, eventCorpseMovedFromFloor);
+ EXPOSE_VALUE(3, field_C);
+ EXPOSE_VALUE(4, eventCorpseFound);
+ EXPOSE_VALUE(5, field_14);
+ EXPOSE_VALUE(6, field_18);
+ EXPOSE_VALUE(7, portrait);
+ EXPOSE_VALUE(8, eventCorpseThrown);
+ EXPOSE_VALUE(9, field_24);
+ EXPOSE_VALUE(10, field_28);
+ EXPOSE_VALUE(11, chapter);
+ EXPOSE_VALUE(12, field_30);
+ EXPOSE_VALUE(13, eventMetAugust);
+ EXPOSE_VALUE(14, isNightTime);
+ EXPOSE_VALUE(15, field_3C);
+ EXPOSE_VALUE(16, field_40);
+ EXPOSE_VALUE(17, field_44);
+ EXPOSE_VALUE(18, field_48);
+ EXPOSE_VALUE(19, field_4C);
+ EXPOSE_VALUE(20, isTrainRunning);
+ EXPOSE_VALUE(21, field_54);
+ EXPOSE_VALUE(22, field_58);
+ EXPOSE_VALUE(23, field_5C);
+ EXPOSE_VALUE(24, field_60);
+ EXPOSE_VALUE(25, field_64);
+ EXPOSE_VALUE(26, field_68);
+ EXPOSE_VALUE(27, eventMertensAugustWaiting);
+ EXPOSE_VALUE(28, eventMertensKronosInvitation);
+ EXPOSE_VALUE(29, isEggOpen);
+ EXPOSE_VALUE(30, field_78);
+ EXPOSE_VALUE(31, field_7C);
+ EXPOSE_VALUE(32, field_80);
+ EXPOSE_VALUE(33, field_84);
+ EXPOSE_VALUE(34, field_88);
+ EXPOSE_VALUE(35, field_8C);
+ EXPOSE_VALUE(36, field_90);
+ EXPOSE_VALUE(37, field_94);
+ EXPOSE_VALUE(38, field_98);
+ EXPOSE_VALUE(39, field_9C);
+ EXPOSE_VALUE(40, field_A0);
+ EXPOSE_VALUE(41, field_A4);
+ EXPOSE_VALUE(42, field_A8);
+ EXPOSE_VALUE(43, field_AC);
+ EXPOSE_VALUE(44, field_B0);
+ EXPOSE_VALUE(45, field_B4);
+ EXPOSE_VALUE(46, field_B8);
+ EXPOSE_VALUE(47, field_BC);
+ EXPOSE_VALUE(48, field_C0);
+ EXPOSE_VALUE(49, field_C4);
+ EXPOSE_VALUE(50, field_C8);
+ EXPOSE_VALUE(51, field_CC);
+ EXPOSE_VALUE(52, eventMetBoutarel);
+ EXPOSE_VALUE(53, eventMetHadija);
+ EXPOSE_VALUE(54, eventMetYasmin);
+ EXPOSE_VALUE(55, field_DC);
+ EXPOSE_VALUE(56, field_E0);
+ EXPOSE_VALUE(57, field_E4);
+ EXPOSE_VALUE(58, field_E8);
+ EXPOSE_VALUE(59, field_EC);
+ EXPOSE_VALUE(60, field_F0);
+ EXPOSE_VALUE(61, field_F4);
+ EXPOSE_VALUE(62, field_F8);
+ EXPOSE_VALUE(63, field_FC);
+ EXPOSE_VALUE(64, field_100);
+ EXPOSE_VALUE(65, field_104);
+ EXPOSE_VALUE(66, field_108);
+ EXPOSE_VALUE(67, field_10C);
+ EXPOSE_VALUE(68, field_110);
+ EXPOSE_VALUE(69, field_114);
+ EXPOSE_VALUE(70, field_118);
+ EXPOSE_VALUE(71, field_11C);
+ EXPOSE_VALUE(72, field_120);
+ EXPOSE_VALUE(73, field_124);
+ EXPOSE_VALUE(74, field_128);
+ EXPOSE_VALUE(75, field_12C);
+ EXPOSE_VALUE(76, field_130);
+ EXPOSE_VALUE(77, field_134);
+ EXPOSE_VALUE(78, field_138);
+ EXPOSE_VALUE(79, field_13C);
+ EXPOSE_VALUE(80, field_140);
+ EXPOSE_VALUE(81, field_144);
+ EXPOSE_VALUE(82, field_148);
+ EXPOSE_VALUE(83, field_14C);
+ EXPOSE_VALUE(84, field_150);
+ EXPOSE_VALUE(85, field_154);
+ EXPOSE_VALUE(86, field_158);
+ EXPOSE_VALUE(87, field_15C);
+ EXPOSE_VALUE(88, field_160);
+ EXPOSE_VALUE(89, field_164);
+ EXPOSE_VALUE(90, field_168);
+ EXPOSE_VALUE(91, field_16C);
+ EXPOSE_VALUE(92, field_170);
+ EXPOSE_VALUE(93, field_174);
+ EXPOSE_VALUE(94, field_178);
+ EXPOSE_VALUE(95, field_17C);
+ EXPOSE_VALUE(96, field_180);
+ EXPOSE_VALUE(97, field_184);
+ EXPOSE_VALUE(98, field_188);
+ EXPOSE_VALUE(99, field_18C);
+ EXPOSE_VALUE(100, field_190);
+ EXPOSE_VALUE(101, field_194);
+ EXPOSE_VALUE(102, field_198);
+ EXPOSE_VALUE(103, field_19C);
+ EXPOSE_VALUE(104, field_1A0);
+ EXPOSE_VALUE(105, field_1A4);
+ EXPOSE_VALUE(106, field_1A8);
+ EXPOSE_VALUE(107, field_1AC);
+ EXPOSE_VALUE(108, field_1B0);
+ EXPOSE_VALUE(109, field_1B4);
+ EXPOSE_VALUE(110, field_1B8);
+ EXPOSE_VALUE(111, field_1BC);
+ EXPOSE_VALUE(112, field_1C0);
+ EXPOSE_VALUE(113, field_1C4);
+ EXPOSE_VALUE(114, field_1C8);
+ EXPOSE_VALUE(115, field_1CC);
+ EXPOSE_VALUE(116, field_1D0);
+ EXPOSE_VALUE(117, field_1D4);
+ EXPOSE_VALUE(118, field_1D8);
+ EXPOSE_VALUE(119, field_1DC);
+ EXPOSE_VALUE(120, field_1E0);
+ EXPOSE_VALUE(121, field_1E4);
+ EXPOSE_VALUE(122, field_1E8);
+ EXPOSE_VALUE(123, field_1EC);
+ EXPOSE_VALUE(124, field_1F0);
+ EXPOSE_VALUE(125, field_1F4);
+ EXPOSE_VALUE(126, field_1F8);
+ EXPOSE_VALUE(127, field_1FC);
+ }
+ }
+
+ Common::String toString() {
+ Common::String ret = "";
+
+ for (uint i = 0; i < 128; i++) {
+ Common::String name = "";
+ uint val = getValueName(i, &name);
+ ret += Common::String::printf("(%03d) %s = %d\n", i, name.c_str(), val);
+ }
+
+ return ret;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ for (uint i = 0; i < 128; i++) {
+ uint32 val = getValueName(i);
+ s.syncAsUint32LE(val);
+ }
+ }
+ };
+
+ struct GameState : public Common::Serializable {
+ // Header
+ uint32 brightness;
+ uint32 volume;
+
+ // Game data
+ uint32 field_0;
+ TimeValue time;
+ uint32 timeDelta;
+ uint32 timeTicks;
+ bool sceneUseBackup; // byte
+ SceneIndex scene; // uint32
+ SceneIndex sceneBackup; // uint32
+ SceneIndex sceneBackup2; // uin32
+
+ GameProgress progress;
+ byte events[512];
+
+ GameState() {
+ brightness = _defaultBrigthness;
+ volume = _defaultVolume;
+
+ //Game data
+ time = kTimeCityParis;
+ timeDelta = _defaultTimeDelta;
+ timeTicks = 0;
+ sceneUseBackup = false;
+ scene = kSceneDefault;
+ sceneBackup = kSceneNone;
+ sceneBackup2 = kSceneNone;
+
+ // Clear game events
+ memset(events, 0, 512*sizeof(byte));
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String ret = "";
+
+ uint8 hours = 0;
+ uint8 minutes = 0;
+ getHourMinutes(time, &hours, &minutes);
+
+ ret += Common::String::printf("Time: %d (%d:%d) - Time delta: %d - Ticks: %d\n", time, hours, minutes, timeDelta, timeTicks);
+ ret += Common::String::printf("Brightness: %d - Volume: %d - UseBackup: %d\n", brightness, volume, sceneUseBackup);
+ ret += Common::String::printf("Scene: %d - Scene backup: %d - Scene backup 2: %d\n", scene, sceneBackup, sceneBackup2);
+
+ return ret;
+ }
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint32LE(time);
+ s.syncAsUint32LE(timeDelta);
+ s.syncAsUint32LE(timeTicks);
+ s.syncAsUint32LE(scene);
+ s.syncAsByte(sceneUseBackup);
+ s.syncAsUint32LE(sceneBackup);
+ s.syncAsUint32LE(sceneBackup2);
+ }
+
+ void syncEvents(Common::Serializer &s) {
+ for (uint i = 0; i < ARRAYSIZE(events); i++)
+ s.syncAsByte(events[i]);
+ }
+ };
+
+ struct Flags {
+ bool flag_0;
+ bool flag_3;
+ bool flag_4;
+ bool flag_5;
+
+ bool frameInterval;
+
+ bool isGameRunning;
+
+ // Mouse flags
+ bool mouseLeftClick;
+ bool mouseRightClick;
+
+ bool flag_entities_0;
+ bool flag_entities_1;
+
+ bool shouldRedraw;
+ bool shouldDrawEggOrHourGlass;
+
+ Flags() {
+ flag_0 = false;
+ flag_3 = false;
+ flag_4 = false;
+ flag_5 = false;
+
+ frameInterval = false;
+
+ isGameRunning = false;
+
+ mouseRightClick = false;
+ mouseLeftClick = false;
+
+ flag_entities_0 = false;
+ flag_entities_1 = false;
+
+ shouldRedraw = false;
+ shouldDrawEggOrHourGlass = false;
+ }
+
+ /**
+ * Convert this object into a string representation.
+ *
+ * @return A string representation of this object.
+ */
+ Common::String toString() {
+ Common::String ret = "";
+
+ ret += Common::String::printf("Unknown: 0:%02d - 3:%02d - 4:%02d - 5:%02d\n", flag_0, flag_3, flag_4, flag_5);
+ ret += Common::String::printf("FrameInterval: %02d - ShouldRedraw:%02d - ShouldDrawEggOrHourGlass:%02d\n", frameInterval, shouldRedraw, shouldDrawEggOrHourGlass);
+ ret += Common::String::printf("IsGameRunning: %02d\n", isGameRunning);
+ ret += Common::String::printf("Mouse: RightClick:%02d - LeftClick:%02d\n", mouseRightClick, mouseLeftClick);
+ ret += Common::String::printf("Entities: 0:%02d - 1:%02d\n", flag_entities_0, flag_entities_1);
+
+ return ret;
+ }
+ };
+
+ State(LastExpressEngine *engine);
+ ~State();
+
+ // Accessors
+ Inventory *getGameInventory() { return _inventory; }
+ Objects *getGameObjects() { return _objects; }
+ SavePoints *getGameSavePoints() { return _savepoints; }
+ GameState *getGameState() { return _state; }
+ Flags *getGameFlags() { return _flags; }
+
+ // Time checks
+ bool isNightTime() const;
+
+ // Timer
+ int getTimer() { return _timer; }
+ void setTimer(int val) { _timer = val; }
+
+ // Coordinates
+ void setCoordinates(Common::Point coords) { _coords = coords; }
+ const Common::Point getCoordinates() { return _coords; }
+
+ // Helpers
+ static uint32 getPowerOfTwo(uint32 x);
+ static void getHourMinutes(uint32 time, uint8 *hours, uint8 *minutes);
+
+private:
+ static const uint32 _defaultBrigthness = 3;
+ static const uint32 _defaultVolume = 7;
+ static const uint32 _defaultTimeDelta = 3;
+ static const uint32 _defaultPortrait = 32;
+
+ LastExpressEngine *_engine;
+
+ // Timer
+ int _timer;
+
+ Flags *_flags; ///< Flags
+ Inventory *_inventory; ///< Inventory
+ Objects *_objects; ///< Objects
+ SavePoints *_savepoints; ///< SavePoints
+ GameState *_state; ///< State
+ Common::Point _coords; ///< Current coordinates
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_STATE_H
diff --git a/engines/lastexpress/graphics.cpp b/engines/lastexpress/graphics.cpp
new file mode 100644
index 0000000000..e5a69d16ea
--- /dev/null
+++ b/engines/lastexpress/graphics.cpp
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/graphics.h"
+
+#include "common/system.h"
+
+namespace LastExpress {
+
+#define COLOR_KEY 0xFFFF
+
+GraphicsManager::GraphicsManager() : _changed(false) {
+ _screen.create(640, 480, 2);
+
+ // Create the game surfaces
+ _backgroundA.create(640, 480, 2);
+ _backgroundC.create(640, 480, 2);
+ _overlay.create(640, 480, 2);
+ _inventory.create(640, 480, 2);
+
+ clear(kBackgroundAll);
+}
+
+GraphicsManager::~GraphicsManager() {
+ // Free the game surfaces
+ _screen.free();
+ _backgroundA.free();
+ _backgroundC.free();
+ _overlay.free();
+ _inventory.free();
+}
+
+void GraphicsManager::update() {
+ // Update the screen if needed and reset the status
+ if (_changed) {
+ mergePlanes();
+ updateScreen();
+ _changed = false;
+ }
+}
+
+void GraphicsManager::change() {
+ _changed = true;
+}
+
+void GraphicsManager::clear(BackgroundType type) {
+ clear(type, Common::Rect(640, 480));
+}
+
+void GraphicsManager::clear(BackgroundType type, const Common::Rect &rect) {
+ switch (type) {
+ default:
+ error("GraphicsManager::clear() - Unknown background type: %d", type);
+ break;
+
+ case kBackgroundA:
+ case kBackgroundC:
+ case kBackgroundOverlay:
+ case kBackgroundInventory:
+ getSurface(type)->fillRect(rect, COLOR_KEY);
+ break;
+
+ case kBackgroundAll:
+ _backgroundA.fillRect(rect, COLOR_KEY);
+ _backgroundC.fillRect(rect, COLOR_KEY);
+ _overlay.fillRect(rect, COLOR_KEY);
+ _inventory.fillRect(rect, COLOR_KEY);
+ break;
+ }
+}
+
+bool GraphicsManager::draw(Drawable *drawable, BackgroundType type, bool transition) {
+ // TODO handle transition properly
+ if (transition)
+ clear(type);
+
+ // TODO store rect for later use
+ Common::Rect rect = drawable->draw(getSurface(type));
+
+ return (!rect.isEmpty());
+}
+
+Graphics::Surface *GraphicsManager::getSurface(BackgroundType type) {
+ switch (type) {
+ default:
+ error("GraphicsManager::getSurface() - Unknown surface type: %d", type);
+ break;
+
+ case kBackgroundA:
+ return &_backgroundA;
+
+ case kBackgroundC:
+ return &_backgroundC;
+
+ case kBackgroundOverlay:
+ return &_overlay;
+
+ case kBackgroundInventory:
+ return &_inventory;
+
+ case kBackgroundAll:
+ error("GraphicsManager::getSurface() - cannot return a surface for kBackgroundAll!");
+ break;
+ }
+}
+
+// TODO optimize to only merge dirty rects
+void GraphicsManager::mergePlanes() {
+ // Clear screen surface
+ _screen.fillRect(Common::Rect(640, 480), 0);
+
+ uint16 *screen = (uint16 *)_screen.pixels;
+ uint16 *inventory = (uint16 *)_inventory.pixels;
+ uint16 *overlay = (uint16 *)_overlay.pixels;
+ uint16 *backgroundC = (uint16 *)_backgroundC.pixels;
+ uint16 *backgroundA = (uint16 *)_backgroundA.pixels;
+
+ for (int i = 0; i < 640 * 480; i++) {
+
+ if (*inventory != COLOR_KEY)
+ *screen = *inventory;
+ else if (*overlay != COLOR_KEY)
+ *screen = *overlay;
+ else if (*backgroundA != COLOR_KEY)
+ *screen = *backgroundA;
+ else if (*backgroundC != COLOR_KEY)
+ *screen = *backgroundC;
+ else
+ *screen = 0;
+
+ inventory++;
+ screen++;
+ overlay++;
+ backgroundA++;
+ backgroundC++;
+ }
+}
+
+void GraphicsManager::updateScreen() {
+ g_system->fillScreen(0);
+ g_system->copyRectToScreen((byte *)_screen.getBasePtr(0, 0), 640 * 2, 0, 0, 640, 480);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/graphics.h b/engines/lastexpress/graphics.h
new file mode 100644
index 0000000000..5231d00f6f
--- /dev/null
+++ b/engines/lastexpress/graphics.h
@@ -0,0 +1,76 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_GRAPHICS_H
+#define LASTEXPRESS_GRAPHICS_H
+
+#include "lastexpress/drawable.h"
+
+namespace LastExpress {
+
+class GraphicsManager {
+public:
+ enum BackgroundType {
+ kBackgroundA,
+ kBackgroundC,
+ kBackgroundOverlay,
+ kBackgroundInventory,
+ kBackgroundAll
+ };
+
+ GraphicsManager();
+ ~GraphicsManager();
+
+ // Update the screen
+ void update();
+
+ // Signal a change to the screen, will cause the planes to be remerged
+ void change();
+
+ // Clear some screen parts
+ void clear(BackgroundType type);
+ void clear(BackgroundType type, const Common::Rect &rect);
+
+ // FIXME this is there for animation until we change it to use the graphic surface here instead of its private ones.
+ Graphics::Surface _screen; // Actual screen surface
+
+ bool draw(Drawable *drawable, BackgroundType type, bool transition = false);
+
+private:
+ Graphics::Surface _backgroundA; // Background A
+ Graphics::Surface _backgroundC; // Background C
+ Graphics::Surface _overlay; // Overlay
+ Graphics::Surface _inventory; // Overlay
+
+ void mergePlanes();
+ void updateScreen();
+ Graphics::Surface *getSurface(BackgroundType type);
+
+ bool _changed;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_GRAPHICS_H
diff --git a/engines/lastexpress/helpers.h b/engines/lastexpress/helpers.h
new file mode 100644
index 0000000000..eb54a1a3ce
--- /dev/null
+++ b/engines/lastexpress/helpers.h
@@ -0,0 +1,102 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_HELPERS_H
+#define LASTEXPRESS_HELPERS_H
+
+//////////////////////////////////////////////////////////////////////////
+// Misc helpers
+//////////////////////////////////////////////////////////////////////////
+
+// Misc
+#define getArchive(name) _engine->getResourceManager()->getFileStream(name)
+#define rnd(value) _engine->getRandom().getRandomNumber(value - 1)
+
+// Engine subclasses
+#define getLogic() _engine->getGameLogic()
+#define getMenu() _engine->getGameMenu()
+
+// Logic
+#define getAction() getLogic()->getGameAction()
+#define getBeetle() getLogic()->getGameBeetle()
+#define getFight() getLogic()->getGameFight()
+#define getEntities() getLogic()->getGameEntities()
+#define getSaveLoad() getLogic()->getGameSaveLoad()
+#define isNight() getLogic()->getGameState()->isNightTime()
+
+// State
+#define getState() getLogic()->getGameState()->getGameState()
+#define getEvent(id) getState()->events[id]
+#define getFlags() getLogic()->getGameState()->getGameFlags()
+#define getInventory() getLogic()->getGameState()->getGameInventory()
+#define getObjects() getLogic()->getGameState()->getGameObjects()
+#define getProgress() getState()->progress
+#define getSavePoints() getLogic()->getGameState()->getGameSavePoints()
+#define getGlobalTimer() getLogic()->getGameState()->getTimer()
+#define setGlobalTimer(timer) getLogic()->getGameState()->setTimer(timer)
+#define setCoords(coords) getLogic()->getGameState()->setCoordinates(coords)
+#define getCoords() getLogic()->getGameState()->getCoordinates()
+#define setFrameCount(count) _engine->setFrameCounter(count)
+#define getFrameCount() _engine->getFrameCounter()
+
+// Scenes
+#define getScenes() _engine->getSceneManager()
+
+// Sound
+#define getSound() _engine->getSoundManager()
+
+// Others
+#define getEntityData(entity) getEntities()->getData(entity)
+
+//////////////////////////////////////////////////////////////////////////
+// Graphics
+//////////////////////////////////////////////////////////////////////////
+
+// Sequences
+#define loadSequence(name) Sequence::load(name, getArchive(name))
+#define loadSequence1(name, field30) Sequence::load(name, getArchive(name), field30)
+
+#define clearBg(type) _engine->getGraphicsManager()->clear(type)
+#define showScene(index, type) _engine->getGraphicsManager()->draw(getScenes()->get(index), type);
+
+#define askForRedraw() _engine->getGraphicsManager()->change();
+#define redrawScreen() _engine->getGraphicsManager()->update(); _engine->_system->updateScreen();
+
+// Used to delete entity sequences
+#define SAFE_DELETE(_p) { if(_p) { delete (_p); (_p) = NULL; } }
+
+//////////////////////////////////////////////////////////////////////////
+// Output
+//////////////////////////////////////////////////////////////////////////
+extern const char *g_actionNames[];
+extern const char *g_directionNames[];
+extern const char *g_entityNames[];
+
+#define ACTION_NAME(action) (action > 18 ? Common::String::printf("%d", action).c_str() : g_actionNames[action])
+#define DIRECTION_NAME(direction) (direction >= 6 ? "INVALID" : g_directionNames[direction])
+#define ENTITY_NAME(index) (index >= 40 ? "INVALID" : g_entityNames[index])
+
+
+#endif // LASTEXPRESS_HELPERS_H
diff --git a/engines/lastexpress/lastexpress.cpp b/engines/lastexpress/lastexpress.cpp
new file mode 100644
index 0000000000..2ccdc14fbd
--- /dev/null
+++ b/engines/lastexpress/lastexpress.cpp
@@ -0,0 +1,310 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/lastexpress.h"
+
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/game/logic.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/state.h"
+#include "lastexpress/game/sound.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/resource.h"
+
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/EventRecorder.h"
+
+#include "engines/util.h"
+
+const char *g_actionNames[] = {"None", "Action1", "Action2", "ExitCompartment", "Action4", "ExcuseMeCath", "ExcuseMe", "INVALID", "Knock", "OpenDoor", "Action10", "Action11", "Default", "INVALID", "INVALID", "INVALID", "Action16", "DrawScene", "Callback"};
+const char *g_directionNames[] = { "None", "Up", "Down", "Left", "Right", "Switch"};
+const char *g_entityNames[] = { "Player", "Anna", "August", "Mertens", "Coudert", "Pascale", "Servers0", "Servers1", "Cooks", "Verges", "Tatiana", "Vassili", "Alexei", "Abbot", "Milos", "Vesna", "Ivo", "Salko", "Kronos", "Kahina", "Francois", "MmeBoutarel", "Boutarel", "Rebecca", "Sophie", "Mahmud", "Yasmin", "Hadija", "Alouan", "Gendarmes", "Max", "Chapters", "Train", "Tables0", "Tables1", "Tables2", "Tables3", "Tables4", "Tables5", "Entity39"};
+
+
+namespace LastExpress {
+
+LastExpressEngine::LastExpressEngine(OSystem *syst, const ADGameDescription *gd) :
+ Engine(syst), _gameDescription(gd), _debugger(NULL), _cursor(NULL),
+ _font(NULL), _logic(NULL), _menu(NULL), _frameCounter(0), _lastFrameCount(0),
+ _graphicsMan(NULL), _resMan(NULL), _sceneMan(NULL), _soundMan(NULL),
+ eventMouse(NULL), eventTick(NULL), eventMouseBackup(NULL), eventTickBackup(NULL) {
+
+ // Adding the default directories
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "data");
+
+ // Initialize the custom debug levels
+ DebugMan.addDebugChannel(kLastExpressDebugAll, "All", "Debug everything");
+ DebugMan.addDebugChannel(kLastExpressDebugGraphics, "Graphics", "Debug graphics & animation/sequence playback");
+ DebugMan.addDebugChannel(kLastExpressDebugResource, "Resource", "Debug resource management");
+ DebugMan.addDebugChannel(kLastExpressDebugCursor, "Cursor", "Debug cursor handling");
+ DebugMan.addDebugChannel(kLastExpressDebugSound, "Sound", "Debug sound playback");
+ DebugMan.addDebugChannel(kLastExpressDebugSubtitle, "Subtitle", "Debug subtitles");
+ DebugMan.addDebugChannel(kLastExpressDebugSavegame, "Savegame", "Debug savegames");
+ DebugMan.addDebugChannel(kLastExpressDebugLogic, "Logic", "Debug logic");
+ DebugMan.addDebugChannel(kLastExpressDebugScenes, "Scenes", "Debug scenes & hotspots");
+ DebugMan.addDebugChannel(kLastExpressDebugUnknown, "Unknown", "Debug unknown data");
+
+ g_eventRec.registerRandomSource(_random, "lastexpress");
+}
+
+LastExpressEngine::~LastExpressEngine() {
+ _timer->removeTimerProc(&soundTimer);
+
+ // Delete the remaining objects
+ delete _cursor;
+ delete _font;
+ delete _logic;
+ delete _menu;
+ delete _graphicsMan;
+ delete _resMan;
+ delete _sceneMan;
+ delete _soundMan;
+ delete _debugger;
+
+ // Cleanup event handlers
+ SAFE_DELETE(eventMouse);
+ SAFE_DELETE(eventTick);
+ SAFE_DELETE(eventMouseBackup);
+ SAFE_DELETE(eventTickBackup);
+
+ // Zero passed pointers
+ _gameDescription = NULL;
+}
+
+// TODO: which error should we return when some game files are missing/corrupted?
+Common::Error LastExpressEngine::run() {
+ // Initialize the graphics
+ const Graphics::PixelFormat dataPixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ initGraphics(640, 480, true, &dataPixelFormat);
+
+ // We do not support color conversion
+ if (_system->getScreenFormat() != dataPixelFormat)
+ return Common::kUnsupportedColorMode;
+
+ // Create debugger. It requires GFX to be initialized
+ _debugger = new Debugger(this);
+
+ // Start the resource and graphics managers
+ _resMan = new ResourceManager(isDemo());
+ if (!_resMan->loadArchive(kArchiveCd1))
+ return Common::kNoGameDataFoundError;
+
+ _graphicsMan = new GraphicsManager();
+
+ // Load the cursor data
+ _cursor = _resMan->loadCursor();
+ if (!_cursor)
+ return Common::kNoGameDataFoundError;
+
+ // Load the font data
+ _font = _resMan->loadFont();
+ if (!_font)
+ return Common::kNoGameDataFoundError;
+
+ // Start scene manager
+ _sceneMan = new SceneManager(this);
+ _sceneMan->loadSceneDataFile(kArchiveCd1);
+
+ // Game logic
+ _logic = new Logic(this);
+
+ // Start sound manager and setup timer
+ _soundMan = new SoundManager(this);
+ _timer->installTimerProc(&soundTimer, 17, this);
+
+ // Menu
+ _menu = new Menu(this);
+ _menu->show(false, kSavegameTypeIndex, 0);
+
+ while (!shouldQuit()) {
+ _soundMan->updateQueue();
+ _soundMan->updateSubtitles();
+
+ if (handleEvents())
+ continue;
+ }
+
+ return Common::kNoError;
+}
+
+void LastExpressEngine::pollEvents() {
+ Common::Event ev;
+ _eventMan->pollEvent(ev);
+
+ switch (ev.type) {
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool LastExpressEngine::handleEvents() {
+ // Make sure all the subsystems have been initialized
+ if (!_debugger || !_graphicsMan)
+ error("LastExpressEngine::handleEvents: called before the required subsystems have been initialized!");
+
+ // Execute stored commands
+ if (_debugger->hasCommand()) {
+ _debugger->callCommand();
+
+ // re-attach the debugger
+ _debugger->attach();
+ }
+
+ // Show the debugger if required
+ _debugger->onFrame();
+
+ // Handle input
+ Common::Event ev;
+ while (_eventMan->pollEvent(ev)) {
+ switch (ev.type) {
+
+ case Common::EVENT_KEYDOWN:
+ // CTRL-D: Attach the debugger
+ if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
+ _debugger->attach();
+
+ //// DEBUG: Quit game on escape
+ //if (ev.kbd.keycode == Common::KEYCODE_ESCAPE)
+ // quitGame();
+
+ break;
+
+ case Common::EVENT_MAINMENU:
+ // Closing the GMM
+
+ case Common::EVENT_LBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseLeftClick = true;
+
+ // Adjust frameInterval flag
+ if (_frameCounter < _lastFrameCount + 30)
+ getGameLogic()->getGameState()->getGameFlags()->frameInterval = true;
+ _lastFrameCount = _frameCounter;
+
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_RBUTTONUP:
+ getGameLogic()->getGameState()->getGameFlags()->mouseRightClick = true;
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ if (eventMouse && eventMouse->isValid())
+ (*eventMouse)(ev);
+ break;
+
+ case Common::EVENT_QUIT:
+ quitGame();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Game tick event
+ if (eventTick && eventTick->isValid())
+ (*eventTick)(ev);
+
+ // Update the screen
+ _graphicsMan->update();
+ _system->updateScreen();
+ _system->delayMillis(50);
+
+ // The event loop may have triggered the quit status. In this case,
+ // stop the execution.
+ if (shouldQuit()) {
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Timer
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::soundTimer(void *refCon) {
+ ((LastExpressEngine *)refCon)->handleSoundTimer();
+}
+
+void LastExpressEngine::handleSoundTimer() {
+ if (_frameCounter & 1)
+ if (_soundMan)
+ _soundMan->handleTimer();
+
+ _frameCounter++;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Event Handling
+///////////////////////////////////////////////////////////////////////////////////
+void LastExpressEngine::backupEventHandlers() {
+ eventMouseBackup = eventMouse;
+ eventTickBackup = eventTick;
+}
+
+void LastExpressEngine::restoreEventHandlers() {
+ if (eventMouseBackup == NULL || eventTickBackup == NULL)
+ error("LastExpressEngine::restoreEventHandlers: restore called before backing up the event handlers!");
+
+ eventMouse = eventMouseBackup;
+ eventTick = eventTickBackup;
+}
+
+void LastExpressEngine::setEventHandlers(EventHandler::EventFunction *mouse, EventHandler::EventFunction *tick) {
+ eventMouse = mouse;
+ eventTick = tick;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+/// Misc Engine
+///////////////////////////////////////////////////////////////////////////////////
+bool LastExpressEngine::hasFeature(EngineFeature f) const {
+ return (f == kSupportsRTL);
+}
+
+void LastExpressEngine::errorString(const char *buf_input, char *buf_output, int buf_output_size) {
+ snprintf(buf_output, (uint)buf_output_size, "%s", buf_input);
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/lastexpress.h b/engines/lastexpress/lastexpress.h
new file mode 100644
index 0000000000..ac05c5a405
--- /dev/null
+++ b/engines/lastexpress/lastexpress.h
@@ -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$
+ *
+ */
+
+#ifndef LASTEXPRESS_H
+#define LASTEXPRESS_H
+
+#include "lastexpress/debug.h"
+#include "lastexpress/eventhandler.h"
+
+#include "common/random.h"
+#include "common/timer.h"
+
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+
+#include "graphics/pixelformat.h"
+
+/**
+ * This is the namespace of the LastExpress engine.
+ *
+ * Status of this engine:
+ * The game is playable but still very buggy and missing crucial functionality:
+ * - Resources: classes for the resource formats used by the game are mostly
+ * complete (subtitles integration/cursor transparency are missing)
+ * - Display: basic graphic manager functionality is implemented (transitions
+ * and dirty rects handling are missing)
+ * - Menu/Navigation: menu is done and navigation/hotspot handling are also
+ * mostly implemented (with remaining bugs)
+ * - Logic: all the hardcoded AI logic has been implemented, as well as the
+ * shared entity code for drawing/handling of entities.
+ * - Sound: most of the sound queue functionality is still missing
+ * - Savegame: almost all the savegame code is still missing.
+ *
+ * Maintainers:
+ * littleboy, jvprat, clone2727
+ *
+ * Supported games:
+ * - The Last Express
+ */
+namespace LastExpress {
+
+class Cursor;
+class Font;
+class GraphicsManager;
+class Logic;
+class Menu;
+class ResourceManager;
+class SceneManager;
+class SoundManager;
+
+class LastExpressEngine : public Engine {
+protected:
+ // Engine APIs
+ Common::Error run();
+ virtual void errorString(const char *buf_input, char *buf_output, int buf_output_size);
+ virtual bool hasFeature(EngineFeature f) const;
+ virtual Debugger *getDebugger() { return _debugger; }
+
+public:
+ LastExpressEngine(OSystem *syst, const ADGameDescription *gd);
+ ~LastExpressEngine();
+
+ // Misc
+ Common::RandomSource getRandom() const {return _random; }
+
+ // Game
+ Cursor *getCursor() const { return _cursor; }
+ Font *getFont() const { return _font; }
+ Logic *getGameLogic() const { return _logic; }
+ Menu *getGameMenu() const { return _menu; }
+
+ // Managers
+ GraphicsManager *getGraphicsManager() const { return _graphicsMan; }
+ ResourceManager *getResourceManager() const { return _resMan; }
+ SceneManager *getSceneManager() const { return _sceneMan; }
+ SoundManager *getSoundManager() const { return _soundMan; }
+
+ // Event handling
+ bool handleEvents();
+ void pollEvents();
+
+ void backupEventHandlers();
+ void restoreEventHandlers();
+ void setEventHandlers(EventHandler::EventFunction *eventMouse, EventHandler::EventFunction *eventTick);
+
+ bool isDemo() const { return (bool)(_gameDescription->flags & ADGF_DEMO); }
+
+ // Frame Counter
+ uint32 getFrameCounter() { return _frameCounter; }
+ void setFrameCounter(uint32 count) { _frameCounter = count; }
+
+protected:
+ // Sound Timer
+ static void soundTimer(void *ptr);
+ void handleSoundTimer();
+
+private:
+ const ADGameDescription *_gameDescription;
+ Graphics::PixelFormat _pixelFormat;
+
+ // Misc
+ Debugger *_debugger;
+ Common::RandomSource _random;
+
+ // Game
+ Cursor *_cursor;
+ Font *_font;
+ Logic *_logic;
+ Menu *_menu;
+
+ // Frame counter
+ uint32 _frameCounter;
+ uint32 _lastFrameCount;
+
+ // Managers
+ GraphicsManager *_graphicsMan;
+ ResourceManager *_resMan;
+ SceneManager *_sceneMan;
+ SoundManager *_soundMan;
+
+ // Event handlers
+ EventHandler::EventFunction *eventMouse;
+ EventHandler::EventFunction *eventTick;
+
+ EventHandler::EventFunction *eventMouseBackup;
+ EventHandler::EventFunction *eventTickBackup;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_H
diff --git a/engines/lastexpress/module.mk b/engines/lastexpress/module.mk
new file mode 100644
index 0000000000..12fbb4f85b
--- /dev/null
+++ b/engines/lastexpress/module.mk
@@ -0,0 +1,73 @@
+MODULE := engines/lastexpress
+
+MODULE_OBJS := \
+ data/animation.o \
+ data/archive.o \
+ data/background.o \
+ data/cursor.o \
+ data/font.o \
+ data/scene.o \
+ data/sequence.o \
+ data/snd.o \
+ data/subtitle.o \
+ entities/entity.o \
+ entities/abbot.o \
+ entities/alexei.o \
+ entities/alouan.o \
+ entities/anna.o \
+ entities/august.o \
+ entities/boutarel.o \
+ entities/chapters.o \
+ entities/cooks.o \
+ entities/coudert.o \
+ entities/entity39.o \
+ entities/francois.o \
+ entities/gendarmes.o \
+ entities/hadija.o \
+ entities/ivo.o \
+ entities/kahina.o \
+ entities/kronos.o \
+ entities/mahmud.o \
+ entities/max.o \
+ entities/mertens.o \
+ entities/milos.o \
+ entities/mmeboutarel.o \
+ entities/pascale.o \
+ entities/rebecca.o \
+ entities/salko.o \
+ entities/servers0.o \
+ entities/servers1.o \
+ entities/sophie.o \
+ entities/tables.o \
+ entities/tatiana.o \
+ entities/train.o \
+ entities/vassili.o \
+ entities/verges.o \
+ entities/vesna.o \
+ entities/yasmin.o \
+ game/action.o \
+ game/beetle.o \
+ game/entities.o \
+ game/fight.o \
+ game/inventory.o \
+ game/logic.o \
+ game/menu.o \
+ game/object.o \
+ game/savegame.o \
+ game/savepoint.o \
+ game/scenes.o \
+ game/sound.o \
+ game/state.o \
+ debug.o \
+ detection.o \
+ graphics.o \
+ lastexpress.o \
+ resource.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_LASTEXPRESS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk \ No newline at end of file
diff --git a/engines/lastexpress/resource.cpp b/engines/lastexpress/resource.cpp
new file mode 100644
index 0000000000..57bc12a185
--- /dev/null
+++ b/engines/lastexpress/resource.cpp
@@ -0,0 +1,248 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lastexpress/resource.h"
+
+#include "lastexpress/data/background.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/font.h"
+
+#include "lastexpress/debug.h"
+
+#include "common/debug.h"
+#include "common/file.h"
+
+namespace LastExpress {
+
+const char *archiveDemoPath = "demo.hpf";
+const char *archiveHDPath = "hd.hpf";
+const char *archiveCD1Path = "cd1.hpf";
+const char *archiveCD2Path = "cd2.hpf";
+const char *archiveCD3Path = "cd3.hpf";
+
+ResourceManager::ResourceManager(bool isDemo) : _isDemo(isDemo) {
+}
+
+ResourceManager::~ResourceManager() {
+ reset();
+}
+
+bool ResourceManager::isArchivePresent(ArchiveIndex type) {
+ switch (type) {
+ default:
+ case kArchiveAll:
+ error("ResourceManager::isArchivePresent: Only checks for single CDs are valid!");
+
+ case kArchiveCd1:
+ return Common::File::exists(archiveCD1Path);
+
+ case kArchiveCd2:
+ return Common::File::exists(archiveCD2Path);
+
+ case kArchiveCd3:
+ return Common::File::exists(archiveCD3Path);
+ }
+}
+
+// Load a specific archive collection
+// - type is ignored in the demo version
+// - use ArchiveAll to load all three cds
+// - HD.hpf is always loaded along with the selected archive(s)
+// - will remove all other archives
+bool ResourceManager::loadArchive(ArchiveIndex type) {
+ // Unload all archives
+ reset();
+
+ // Demo version
+ if (_isDemo)
+ return loadArchive(archiveDemoPath);
+
+ // Load HD
+ if (!loadArchive(archiveHDPath))
+ return false;
+
+ switch(type) {
+ case kArchiveCd1:
+ return loadArchive(archiveCD1Path);
+
+ case kArchiveCd2:
+ return loadArchive(archiveCD2Path);
+
+ case kArchiveCd3:
+ return loadArchive(archiveCD3Path);
+
+ case kArchiveAll:
+ default:
+ if (loadArchive(archiveCD1Path))
+ if (loadArchive(archiveCD2Path))
+ return loadArchive(archiveCD3Path);
+ break;
+ }
+
+ return false;
+}
+
+void ResourceManager::reset() {
+ // Free the loaded archives
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it)
+ delete (*it);
+
+ _archives.clear();
+}
+
+bool ResourceManager::loadArchive(const Common::String &name) {
+ HPFArchive *archive = new HPFArchive(name);
+
+ if (archive->count() == 0) {
+ debugC(2, kLastExpressDebugResource, "Error opening archive: %s", name.c_str());
+
+ delete archive;
+
+ return false;
+ }
+
+ _archives.push_back(archive);
+
+ return true;
+}
+
+// Get a stream to file in the archive
+// - same as createReadStreamForMember except it checks if the file exists and will assert / output a debug message if not
+Common::SeekableReadStream *ResourceManager::getFileStream(const Common::String &name) {
+
+ // Check if the file exits in the archive
+ if (!hasFile(name)) {
+//#ifdef _DEBUG
+// error("ResourceManager::getFileStream: cannot open file: %s", name.c_str());
+//#endif
+ debugC(2, kLastExpressDebugResource, "Error opening file: %s", name.c_str());
+ return NULL;
+ }
+
+ debugC(2, kLastExpressDebugResource, "Opening file: %s", name.c_str());
+
+ return createReadStreamForMember(name);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Archive functions
+//////////////////////////////////////////////////////////////////////////
+bool ResourceManager::hasFile(const Common::String &name) {
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+ if ((*it)->hasFile(name))
+ return true;
+ }
+
+ return false;
+}
+
+int ResourceManager::listMembers(Common::ArchiveMemberList &list) {
+ int count = 0;
+
+ for (Common::Array<HPFArchive *>::iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::ArchiveMemberList members;
+ count += (*it)->listMembers(members);
+
+ list.insert(list.end(), members.begin(), members.end());
+ }
+
+ return count;
+}
+
+Common::ArchiveMemberPtr ResourceManager::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *ResourceManager::createReadStreamForMember(const Common::String &name) const {
+ for (Common::Array<HPFArchive *>::const_iterator it = _archives.begin(); it != _archives.end(); ++it) {
+
+ Common::SeekableReadStream *stream = (*it)->createReadStreamForMember(name);
+
+ if (stream)
+ return stream;
+ }
+
+ return NULL;
+}
+
+
+// Resource loading
+
+Background *ResourceManager::loadBackground(const Common::String &name) const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember(name + ".bg");
+ if (!stream)
+ return NULL;
+
+ // Create the new background & load the data
+ Background *bg = new Background();
+ if (!bg->load(stream)) {
+ delete bg;
+ // stream should be freed by the Background instance
+ return NULL;
+ }
+
+ return bg;
+}
+
+Cursor *ResourceManager::loadCursor() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("cursors.tbm");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Cursor *c = new Cursor();
+ if (!c->load(stream)) {
+ delete c;
+ // stream should be freed by the Cursor instance
+ return NULL;
+ }
+
+ return c;
+}
+
+Font *ResourceManager::loadFont() const {
+ // Open the resource
+ Common::SeekableReadStream *stream = createReadStreamForMember("font.dat");
+ if (!stream)
+ return NULL;
+
+ // Create the new background
+ Font *f = new Font();
+ if (!f->load(stream)) {
+ delete f;
+ // stream should be freed by the Font instance
+ return NULL;
+ }
+
+ return f;
+}
+
+} // End of namespace LastExpress
diff --git a/engines/lastexpress/resource.h b/engines/lastexpress/resource.h
new file mode 100644
index 0000000000..ea6508edc3
--- /dev/null
+++ b/engines/lastexpress/resource.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$
+ *
+ */
+
+#ifndef LASTEXPRESS_RESOURCE_H
+#define LASTEXPRESS_RESOURCE_H
+
+#include "lastexpress/data/archive.h"
+#include "lastexpress/shared.h"
+
+namespace LastExpress {
+
+class Background;
+class Cursor;
+class Font;
+
+class ResourceManager : public Common::Archive {
+public:
+ ResourceManager(bool demo);
+ ~ResourceManager();
+
+ // Loading
+ bool loadArchive(ArchiveIndex type);
+ static bool isArchivePresent(ArchiveIndex type);
+ Common::SeekableReadStream *getFileStream(const Common::String &name);
+
+ // Archive functions
+ 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;
+
+ // Resource loading
+ Background *loadBackground(const Common::String &name) const;
+ Cursor *loadCursor() const;
+ Font *loadFont() const;
+
+private:
+ bool _isDemo;
+
+ bool loadArchive(const Common::String &name);
+ void reset();
+
+ Common::Array<HPFArchive *> _archives;
+
+ friend class Debugger;
+};
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_RESOURCE_H
diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h
new file mode 100644
index 0000000000..80e227e6a7
--- /dev/null
+++ b/engines/lastexpress/shared.h
@@ -0,0 +1,1716 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 LASTEXPRESS_SHARED_H
+#define LASTEXPRESS_SHARED_H
+
+#include "common/func.h"
+
+namespace LastExpress {
+
+//////////////////////////////////////////////////////////////////////////
+// Time values
+//////////////////////////////////////////////////////////////////////////
+
+// Time is measured in ticks, with 15 ticks per second. One minute is 900
+// ticks, one hour is 54,000 ticks, and one day is 1,296,000 ticks.
+
+enum TimeValue {
+ kTimeNone = 0,
+ kTime5933 = 5933,
+
+ kTimeCityParis = 1037700, // Day 1, 19:13
+ kTime1039500 = 1039500, // Day 1, 19:15
+ kTimeStartGame = 1061100, // Day 1, 19:39
+
+ // Chapter 1
+ kTimeChapter1 = 1062000, // Day 1, 19:40
+ kTime1071000 = 1071000, // Day 1, 19:50
+ kTimeParisEpernay = 1075500, // Day 1, 19:55
+ kTime1080000 = 1080000, // Day 1, 20:00
+ kTime1084500 = 1084500, // Day 1, 20:05
+ kTime1089000 = 1089000, // Day 1, 20:10
+ kTime1093500 = 1093500, // Day 1, 20:15
+ kTime1094400 = 1094400, // Day 1, 20:16
+ kTime1096200 = 1096200, // Day 1, 20:18
+ kTime1098000 = 1098000, // Day 1, 20:20
+ kTime1102500 = 1102500, // Day 1, 20:25
+ kTime1107000 = 1107000, // Day 1, 20:30
+ kTime1111500 = 1111500, // Day 1, 20:35
+ kTime1120500 = 1120500, // Day 1, 20:45
+ kTime1125000 = 1125000, // Day 1, 20:50
+ kTime1134000 = 1134000, // Day 1, 21:00
+ kTime1138500 = 1138500, // Day 1, 21:05
+ kTime1143000 = 1143000, // Day 1, 21:10
+ kTimeEnterEpernay = 1147500, // Day 1, 21:15
+ kTimeCityEpernay = 1148400, // Day 1, 21:16
+ kTimeExitEpernay = 1150200, // Day 1, 21:18
+ kTime1156500 = 1156500, // Day 1, 21:25
+ kTime1161000 = 1161000, // Day 1, 21:30
+ kTime1162800 = 1162800, // Day 1, 21:32
+ kTime1165500 = 1165500, // Day 1, 21:35
+ kTime1167300 = 1167300, // Day 1, 21:37
+ kTimeEnterChalons = 1170000, // Day 1, 21:40
+ kTimeCityChalons = 1170900, // Day 1, 21:41
+ kTimeExitChalons = 1173600, // Day 1, 21:44
+ kTime1174500 = 1174500, // Day 1, 21:45
+ kTime1179000 = 1179000, // Day 1, 21:50
+ kTime1183500 = 1183500, // Day 1, 21:55
+ kTime1184400 = 1184400, // Day 1, 21:56
+ kTime1188000 = 1188000, // Day 1, 22:00
+ kTime1189800 = 1189800, // Day 1, 22:02
+ kTime1192500 = 1192500, // Day 1, 22:05
+ kTime1197000 = 1197000, // Day 1, 22:10
+ kTime1201500 = 1201500, // Day 1, 22:15
+ kTime1206000 = 1206000, // Day 1, 22:20
+ kTime1215000 = 1215000, // Day 1, 22:30
+ kTime1224000 = 1224000, // Day 1, 22:40
+ kTime1225800 = 1225800, // Day 1, 22:42
+ kTimeCityBarLeDuc = 1228500, // Day 1, 22:45
+ kTimeExitBarLeDuc = 1231200, // Day 1, 22:48
+ kTime1233000 = 1233000, // Day 1, 22:50
+ kTime1242000 = 1242000, // Day 1, 23:00
+ kTime1260000 = 1260000, // Day 1, 23:20
+ kTimeCityNancy = 1303200, // Day 2, 00:08
+ kTimeExitNancy = 1307700, // Day 2, 00:13
+ kTime1323000 = 1323000, // Day 2, 00:30
+ kTimeCityLuneville = 1335600, // Day 2, 00:44
+ kTimeExitLuneville = 1338300, // Day 2, 00:47
+ kTimeCityAvricourt = 1359900, // Day 2, 01:11
+ kTimeExitAvricourt = 1363500, // Day 2, 01:15
+ kTimeCityDeutschAvricourt = 1367100, // Day 2, 01:19
+ kTimeExitDeutschAvricourt = 1370700, // Day 2, 01:23
+ kTime1386000 = 1386000, // Day 2, 01:40
+ kTimeBedTime = 1404000, // Day 2, 02:00
+ kTime1417500 = 1417500, // Day 2, 02:15
+ kTimeEnterStrasbourg = 1424700, // Day 2, 02:23
+ kTime1449000 = 1449000, // Day 2, 02:50
+ kTime1458000 = 1458000, // Day 2, 03:00
+ kTime1485000 = 1485000, // Day 2, 03:30
+ kTime1489500 = 1489500, // Day 2, 03:35
+ kTimeCityStrasbourg = 1490400, // Day 2, 03:36
+ kTime1492200 = 1492200, // Day 2, 03:38
+ kTimeExitStrasbourg = 1493100, // Day 2, 03:39
+ kTimeChapter1End = 1494000, // Day 2, 03:40
+ kTime1503000 = 1503000, // Day 2, 03:50
+ kTime1512000 = 1512000, // Day 2, 04:00
+ kTimeCityBadenOos = 1539000, // Day 2, 04:30
+ kTimeExitBadenOos = 1541700, // Day 2, 04:33
+ kTimeCityKarlsruhe = 1563300, // Day 2, 04:57
+ kTimeCityStuttgart = 1656000, // Day 2, 06:40
+ kTimeChapter1End2 = 1647000, // Day 2, 06:30
+ kTimeChapter1End3 = 1674000, // Day 2, 07:00
+ kTimeCityGeislingen = 1713600, // Day 2, 07:44
+ kTime1714500 = 1714500, // Day 2, 07:45
+ kTimeCityUlm = 1739700, // Day 2, 08:13
+
+ // Chapter 2
+ kTimeChapter2 = 1750500, // Day 2, 08:25
+ kTime1759500 = 1759500, // Day 2, 08:35
+ kTime1755000 = 1755000, // Day 2, 08:30
+ kTime1764000 = 1764000, // Day 2, 08:40
+ kTime1768500 = 1768500, // Day 2, 08:45
+ kTime1773000 = 1773000, // Day 2, 08:50
+ kTime1777500 = 1777500, // Day 2, 08:55
+ kTime1782000 = 1782000, // Day 2, 09:00
+ kTime1786500 = 1786500, // Day 2, 09:05
+ kTime1791000 = 1791000, // Day 2, 09:10
+ kTime1800000 = 1800000, // Day 2, 09:20
+ kTime1801800 = 1801800, // Day 2, 09:22
+ kTime1806300 = 1806300, // Day 2, 09:27
+ kTime1809000 = 1809000, // Day 2, 09:30
+ kTimeCityAugsburg = 1809900, // Day 2, 09:31
+ kTime1813500 = 1813500, // Day 2, 09:35
+ kTime1818000 = 1818000, // Day 2, 09:40
+ kTime1818900 = 1818900, // Day 2, 09:41
+ kTime1820700 = 1820700, // Day 2, 09:43
+ kTime1822500 = 1822500, // Day 2, 09:45
+ kTime1827000 = 1827000, // Day 2, 09:50
+ kTime1831500 = 1831500, // Day 2, 09:55
+ kTime1836000 = 1836000, // Day 2, 10:00
+ kTime1845000 = 1845000, // Day 2, 10:10
+ kTime1849500 = 1849500, // Day 2, 10:15
+ kTimeCityMunich = 1852200, // Day 2, 10:18
+
+ // Chapter 3
+ kTimeChapter3 = 1944000, // Day 2, 12:00
+ kTime1953000 = 1953000, // Day 2, 12:10
+ kTime1966500 = 1966500, // Day 2, 12:25
+ kTime1969200 = 1969200, // Day 2, 12:28
+ kTime1971000 = 1971000, // Day 2, 12:30
+ kTimeEnterSalzbourg = 1982700, // Day 2, 12:43
+ kTime1983600 = 1983600, // Day 2, 12:44
+ kTimeCitySalzbourg = 1984500, // Day 2, 12:45
+ kTime1989000 = 1989000, // Day 2, 12:50
+ kTimeExitSalzbourg = 1989900, // Day 2, 12:51
+ kTime1993500 = 1993500, // Day 2, 12:55
+ kTime1998000 = 1998000, // Day 2, 13:00
+ kTime2002500 = 2002500, // Day 2, 13:05
+ kTime2011500 = 2011500, // Day 2, 13:15
+ kTime2016000 = 2016000, // Day 2, 13:20
+ kTime2020500 = 2020500, // Day 2, 13:25
+ kTime2025000 = 2025000, // Day 2, 13:30
+ kTime2034000 = 2034000, // Day 2, 13:40
+ kTime2038500 = 2038500, // Day 2, 13:45
+ kTime2040300 = 2040300, // Day 2, 13:47
+ kTime2043000 = 2043000, // Day 2, 13:50
+ kTimeEnterAttnangPuchheim = 2047500, // Day 2, 13:55
+ kTimeCityAttnangPuchheim = 2049300, // Day 2, 13:57
+ kTime2052000 = 2052000, // Day 2, 14:00
+ kTimeExitAttnangPuchheim = 2052900, // Day 2, 14:01
+ kTime2056500 = 2056500, // Day 2, 14:05
+ kTime2061000 = 2061000, // Day 2, 14:10
+ kTime2062800 = 2062800, // Day 2, 14:12
+ kTime2065500 = 2065500, // Day 2, 14:15
+ kTime2070000 = 2070000, // Day 2, 14:20
+ kTimeEnterWels = 2073600, // Day 2, 14:24
+ kTimeCityWels = 2075400, // Day 2, 14:26
+ kTime2079000 = 2079000, // Day 2, 14:30
+ kTimeExitWels = 2079900, // Day 2, 14:31
+ kTime2083500 = 2083500, // Day 2, 14:35
+ kTime2088000 = 2088000, // Day 2, 14:40
+ kTime2088900 = 2088900, // Day 2, 14:41
+ kTime2092500 = 2092500, // Day 2, 14:45
+ kTime2097000 = 2097000, // Day 2, 14:50
+ kTimeEnterLinz = 2099700, // Day 2, 14:53
+ kTimeCityLinz = 2101500, // Day 2, 14:55
+ kTime2106000 = 2106000, // Day 2, 15:00
+ kTime2110500 = 2110500, // Day 2, 15:05
+ kTime2115000 = 2115000, // Day 2, 15:10
+ kTime2117700 = 2117700, // Day 2, 15:13
+ kTime2119500 = 2119500, // Day 2, 15:15
+ kTime2124000 = 2124000, // Day 2, 15:20
+ kTime2133000 = 2133000, // Day 2, 15:30
+ kTime2138400 = 2138400, // Day 2, 15:36
+ kTime2142000 = 2142000, // Day 2, 15:40
+ kTime2146500 = 2146500, // Day 2, 15:45
+ kTime2147400 = 2147400, // Day 2, 15:46
+ kTime2151000 = 2151000, // Day 2, 15:50
+ kTimeCityAmstetten = 2154600, // Day 2, 15:54
+ kTime2155500 = 2155500, // Day 2, 15:55
+ kTime2160000 = 2160000, // Day 2, 16:00
+ kTime2169000 = 2169000, // Day 2, 16:10
+ kTime2173500 = 2173500, // Day 2, 16:15
+ kTime2187000 = 2187000, // Day 2, 16:30
+ kTime2182500 = 2182500, // Day 2, 16:25
+ kTime2196000 = 2196000, // Day 2, 16:40
+ kTime2200500 = 2200500, // Day 2, 16:45
+ kTime2205000 = 2205000, // Day 2, 16:50
+ kTime2214000 = 2214000, // Day 2, 17:00
+ kTime2218500 = 2218500, // Day 2, 17:05
+ kTime2223000 = 2223000, // Day 2, 17:10
+ kTime2227500 = 2227500, // Day 2, 17:15
+ kTime2241000 = 2241000, // Day 2, 17:30
+ kTime2248200 = 2248200, // Day 2, 17:38
+ kTime2250000 = 2250000, // Day 2, 17:40
+ kTime2254500 = 2254500, // Day 2, 17:45
+ kTime2259000 = 2259000, // Day 2, 17:50
+ kTime2263500 = 2263500, // Day 2, 17:55
+ kTime2266200 = 2266200, // Day 2, 17:58
+ kTimeCityVienna = 2268000, // Day 2, 18:00
+
+ // Chapter 4
+ kTime2349000 = 2349000, // Day 2, 19:30
+ kTimeChapter4 = 2353500, // Day 2, 19:35
+ kTime2354400 = 2354400, // Day 2, 19:36
+ kTime2356200 = 2356200, // Day 2, 19:38
+ kTime2358000 = 2358000, // Day 2, 19:40
+ kTime2360700 = 2360700, // Day 2, 19:43
+ kTime2362500 = 2362500, // Day 2, 19:45
+ kTime2361600 = 2361600, // Day 2, 19:44
+ kTime2367000 = 2367000, // Day 2, 19:50
+ kTime2370600 = 2370600, // Day 2, 19:54
+ kTime2378700 = 2378700, // Day 2, 20:03
+ kTimeEnterPoszony = 2381400, // Day 2, 20:06
+ kTimeCityPoszony = 2383200, // Day 2, 20:08
+ kTime2385000 = 2385000, // Day 2, 20:10
+ kTimeExitPoszony = 2386800, // Day 2, 20:12
+ kTime2389500 = 2389500, // Day 2, 20:15
+ kTime2394000 = 2394000, // Day 2, 20:20
+ kTime2398500 = 2398500, // Day 2, 20:25
+ kTime2403000 = 2403000, // Day 2, 20:30
+ kTime2407500 = 2407500, // Day 2, 20:35
+ kTime2410200 = 2410200, // Day 2, 20:38
+ kTime2412000 = 2412000, // Day 2, 20:40
+ kTime2414700 = 2414700, // Day 2, 20:43
+ kTime2415600 = 2415600, // Day 2, 20:44
+ kTimeEnterGalanta = 2416500, // Day 2, 20:45
+ kTimeCityGalanta = 2418300, // Day 2, 20:47
+ kTime2421000 = 2421000, // Day 2, 20:50
+ kTimeExitGalanta = 2421900, // Day 2, 20:51
+ kTime2422800 = 2422800, // Day 2, 20:52
+ kTime2428200 = 2428200, // Day 2, 20:58
+ kTime2425500 = 2425500, // Day 2, 20:55
+ kTime2430000 = 2430000, // Day 2, 21:00
+ kTime2434500 = 2434500, // Day 2, 21:05
+ kTime2439000 = 2439000, // Day 2, 21:10
+ kTime2443500 = 2443500, // Day 2, 21:15
+ kTime2448000 = 2448000, // Day 2, 21:20
+ kTime2452500 = 2452500, // Day 2, 21:25
+ kTime2455200 = 2455200, // Day 2, 21:28
+ kTime2457000 = 2457000, // Day 2, 21:30
+ kTime2466000 = 2466000, // Day 2, 21:40
+ kTime2470500 = 2470500, // Day 2, 21:45
+ kTime2475000 = 2475000, // Day 2, 21:50
+ kTime2479500 = 2479500, // Day 2, 21:55
+ kTime2484000 = 2484000, // Day 2, 22:00
+ kTime2488500 = 2488500, // Day 2, 22:05
+ kTime2493000 = 2493000, // Day 2, 22:10
+ kTime2506500 = 2506500, // Day 2, 22:25
+ kTime2507400 = 2507400, // Day 2, 22:26
+ kTime2511000 = 2511000, // Day 2, 22:30
+ kTime2511900 = 2511900, // Day 2, 22:31
+ kTime2517300 = 2517300, // Day 2, 22:37
+ kTime2519100 = 2519100, // Day 2, 22:39
+ kTime2520000 = 2520000, // Day 2, 22:40
+ kTime2533500 = 2533500, // Day 2, 22:55
+ kTime2535300 = 2535300, // Day 2, 22:57
+ kTime2538000 = 2538000, // Day 2, 23:00
+ kTimeCityBudapest = 2551500, // Day 2, 23:15
+
+ // Chapter 5
+ kTimeChapter5 = 2844000, // Day 3, 04:40
+ kTimeTrainStopped = 2898000, // Day 3, 05:40
+ kTime2907000 = 2907000, // Day 3, 05:50
+ kTime2916000 = 2916000, // Day 3, 06:00
+ kTimeCityBelgrade = 2952000, // Day 3, 06:40
+ kTimeTrainStopped2 = 2943000, // Day 3, 06:30
+ kTime2983500 = 2983500, // Day 3, 07:15
+ kTimeCityNish = 3205800, // Day 3, 11:22
+ kTimeCityTzaribrod = 3492000, // Day 3, 16:40
+ kTime3645000 = 3645000, // Day 3, 19:30
+ kTimeCitySofia = 3690000, // Day 3, 20:20
+ kTimeCityAdrianople = 4320900, // Day 4, 08:01
+ kTime4923000 = 4923000, // Day 4, 19:10
+ kTime4929300 = 4929300, // Day 4, 19:17
+ kTimeCityConstantinople = 4941000, // Day 4, 19:30
+
+
+ kTime10881000 = 10881000,
+ kTimeEnd = 15803100,
+ kTime16451100 = 16451100,
+
+ kTimeInvalid = 2147483647,
+ kTimeInvalid2 = 0xFFFFFEDA
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Archive & Chapter ID
+//////////////////////////////////////////////////////////////////////////
+enum ArchiveIndex {
+ kArchiveAll = 0,
+ kArchiveCd1 = 1,
+ kArchiveCd2 = 2,
+ kArchiveCd3 = 3
+};
+
+enum ChapterIndex {
+ kChapterAll = 0,
+ kChapter1 = 1,
+ kChapter2 = 2,
+ kChapter3 = 3,
+ kChapter4 = 4,
+ kChapter5 = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of scenes
+//////////////////////////////////////////////////////////////////////////
+enum SceneIndex {
+ kSceneNone = 0,
+ kSceneMenu = 1,
+
+ kSceneIntro = 30,
+
+ // Inventory
+ kSceneMatchbox = 31,
+ kSceneTelegram = 32,
+ kScenePassengerList = 33,
+ kSceneScarf = 34,
+ kSceneParchemin = 35,
+ kSceneArticle = 36,
+ kScenePaper = 37,
+ kSceneFirebird = 38,
+ kSceneBriefcase = 39,
+
+ // Normal scenes
+ kSceneDefault = 40,
+ kScene41 = 41,
+ kSceneCompartmentCorpse = 42, // Tyler compartment with corpse on floor
+
+ // Fight
+ kSceneFightMilos = 43,
+ kSceneFightMilosBedOpened = 44,
+ kSceneFightAnna = 45,
+ kSceneFightIvo = 46,
+ kSceneFightSalko = 47,
+ kSceneFightVesna = 48,
+
+ kSceneEuropeMap = 49,
+
+ // Game over
+ kSceneGameOverStopPolice = 50,
+ kSceneGameOverTrainStopped = 51,
+ kSceneGameOverTrainStopped2 = 52,
+ kSceneGameOverTrainExplosion = 53,
+ kSceneGameOverTrainExplosion2 = 54,
+ kSceneGameOverBloodJacket = 55,
+ kSceneGameOverPolice = 56,
+ kSceneGameOverPolice1 = 57,
+ kSceneGameOverAnnaDied = 58,
+ kSceneGameOverVienna = 59,
+ kSceneGameOverVienna1 = 60,
+ kSceneGameOverVienna2 = 61,
+ kSceneGameOverAlarm = 62,
+ kSceneGameOverPolice2 = 63,
+ kSceneGameOverAlarm2 = 64,
+
+ // Start screen
+ kSceneStartScreen = 65,
+
+ kSceneBeetle = 128,
+
+ kSceneFightDefault = 820,
+
+ kSceneInvalid = 0xffffffff
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Jacket
+//////////////////////////////////////////////////////////////////////////
+enum JacketType {
+ kJacketOriginal = 0,
+ kJacketBlood = 1,
+ kJacketGreen = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// City
+//////////////////////////////////////////////////////////////////////////
+enum CityIndex {
+ kCityEpernay = 0,
+ kCityChalons,
+ kCityBarleduc,
+ kCityNancy,
+ kCityLuneville,
+ kCityAvricourt, // 5
+ kCityDeutschAvricourt,
+ kCityStrasbourg,
+ kCityBadenOos,
+ kCitySalzbourg,
+ kCityAttnangPuchheim, // 10
+ kCityWels,
+ kCityLinz,
+ kCityVienna,
+ kCityPoszony,
+ kCityGalanta, // 15
+ kCityPolice
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Savegame ID
+//////////////////////////////////////////////////////////////////////////
+enum GameId {
+ kGameBlue,
+ kGameRed,
+ kGameGreen,
+ kGamePurple,
+ kGameTeal,
+ kGameGold
+};
+
+enum SavegameType {
+ kSavegameTypeIndex = 0,
+ kSavegameTypeTime = 1,
+ kSavegameTypeEvent = 2,
+ kSavegameTypeEvent2 = 3,
+ kSavegameTypeAuto = 4,
+ kSavegameTypeTickInterval = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Cursor style
+//////////////////////////////////////////////////////////////////////////
+enum CursorStyle {
+ kCursorNormal,
+ kCursorForward,
+ kCursorBackward,
+ kCursorTurnRight,
+ kCursorTurnLeft,
+ kCursorUp,
+ kCursorDown,
+ kCursorLeft,
+ kCursorRight,
+ kCursorHand,
+ kCursorHandKnock, // 10
+ kCursorMagnifier,
+ kCursorHandPointer,
+ kCursorSleep,
+ kCursorTalk,
+ kCursorTalk2, // Need better name
+
+ // Items
+ kCursorMatchBox,
+ kCursorTelegram,
+ kCursorPassengerList,
+ kCursorArticle,
+ kCursorScarf, // 20
+ kCursorPaper,
+ kCursorParchemin,
+ kCursorMatch,
+ kCursorWhistle,
+ kCursorKey,
+ kCursorBomb,
+ kCursorFirebird,
+ kCursorBriefcase,
+ kCursorCorpse,
+
+ // Combat
+ kCursorPunchLeft, // 30
+ kCursorPunchRight,
+
+ // Portraits
+ kCursorPortrait, // 32
+ kCursorPortraitSelected,
+ kCursorPortraitGreen,
+ kCursorPortraitGreenSelected,
+ kCursorPortraitYellow,
+ kCursorPortraitYellowSelected,
+ kCursorHourGlass,
+ kCursorEggBlue,
+ kCursorEggRed, // 40
+ kCursorEggGreen,
+ kCursorEggPurple,
+ kCursorEggTeal,
+ kCursorEggGold,
+ kCursorEggClock,
+ kCursorNormal2,
+ kCursorBlank,
+ kCursorMAX,
+
+ // Special
+ kCursorProcess = 128,
+ kCursorKeepValue = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Position - should be between 0 & 100
+//////////////////////////////////////////////////////////////////////////
+typedef unsigned char Position;
+
+//////////////////////////////////////////////////////////////////////////
+// EntityPosition
+//////////////////////////////////////////////////////////////////////////
+enum EntityPosition {
+ kPositionNone = 0,
+ kPosition_1 = 1,
+ kPosition_3 = 3,
+ kPosition_4 = 4,
+ kPosition_500 = 500,
+ kPosition_540 = 540,
+ kPosition_750 = 750,
+ kPosition_849 = 849,
+ kPosition_850 = 850,
+ kPosition_851 = 851,
+ kPosition_1200 = 1200,
+ kPosition_1430 = 1430,
+ kPosition_1500 = 1500,
+ kPosition_1540 = 1540,
+ kPosition_1750 = 1750,
+ kPosition_2000 = 2000,
+ kPosition_2087 = 2087,
+ kPosition_2086 = 2086,
+ kPosition_2088 = 2088,
+ kPosition_2110 = 2110,
+ kPosition_2300 = 2300,
+ kPosition_2330 = 2330,
+ kPosition_2410 = 2410,
+ kPosition_2436 = 2436,
+ kPosition_2490 = 2490,
+ kPosition_2500 = 2500,
+ kPosition_2587 = 2587,
+ kPosition_2588 = 2588,
+ kPosition_2690 = 2690,
+ kPosition_2740 = 2740,
+ kPosition_2830 = 2830,
+ kPosition_2980 = 2980,
+ kPosition_3050 = 3050,
+ kPosition_3110 = 3110,
+ kPosition_3390 = 3390,
+ kPosition_3450 = 3450,
+ kPosition_3500 = 3500,
+ kPosition_3550 = 3550,
+ kPosition_3650 = 3650,
+ kPosition_3760 = 3760,
+ kPosition_3820 = 3820,
+ kPosition_3890 = 3890,
+ kPosition_3969 = 3969,
+ kPosition_3970 = 3970,
+ kPosition_4070 = 4070,
+ kPosition_4100 = 4100,
+ kPosition_4370 = 4370,
+ kPosition_4455 = 4455,
+ kPosition_4460 = 4460,
+ kPosition_4500 = 4500,
+ kPosition_4590 = 4590,
+ kPosition_4680 = 4680,
+ kPosition_4689 = 4689,
+ kPosition_4690 = 4690,
+ kPosition_4691 = 4691,
+ kPosition_4770 = 4470,
+ kPosition_4840 = 4840,
+ kPosition_5000 = 5000,
+ kPosition_5090 = 5090,
+ kPosition_5140 = 5140,
+ kPosition_5419 = 5419,
+ kPosition_5420 = 5420,
+ kPosition_5440 = 5440,
+ kPosition_5500 = 5500,
+ kPosition_5540 = 5540,
+ kPosition_5610 = 5610,
+ kPosition_5790 = 5790,
+ kPosition_5799 = 5799,
+ kPosition_5800 = 5800,
+ kPosition_5810 = 5810,
+ kPosition_5890 = 5890,
+ kPosition_5900 = 5900,
+ kPosition_5970 = 5970,
+ kPosition_6000 = 6000,
+ kPosition_6130 = 6130,
+ kPosition_6160 = 6160,
+ kPosition_6220 = 6220,
+ kPosition_6410 = 6410,
+ kPosition_6460 = 6460,
+ kPosition_6469 = 6469,
+ kPosition_6470 = 6470,
+ kPosition_6471 = 6471,
+ kPosition_6800 = 6800,
+ kPosition_6850 = 6850,
+ kPosition_7000 = 7000,
+ kPosition_7160 = 7160,
+ kPosition_7250 = 7250,
+ kPosition_7320 = 7320,
+ kPosition_7500 = 7500,
+ kPosition_7510 = 7510,
+ kPosition_7850 = 7850,
+ kPosition_7870 = 7870,
+ kPosition_7900 = 7900,
+ kPosition_7950 = 7950,
+ kPosition_8000 = 8000,
+ kPosition_8012 = 8012,
+ kPosition_8013 = 8013,
+ kPosition_8160 = 8160,
+ kPosition_8200 = 8200,
+ kPosition_8500 = 8500,
+ kPosition_8512 = 8512,
+ kPosition_8513 = 8513,
+ kPosition_8514 = 8514,
+ kPosition_8800 = 8800,
+ kPosition_9020 = 9020,
+ kPosition_9269 = 9269,
+ kPosition_9250 = 9250,
+ kPosition_9270 = 9270,
+ kPosition_9271 = 9271,
+ kPosition_9460 = 9460,
+ kPosition_9500 = 9500,
+ kPosition_9510 = 9510,
+ kPosition_30000 = 30000
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location
+//////////////////////////////////////////////////////////////////////////
+enum Location {
+ kLocationOutsideCompartment = 0,
+ kLocationInsideCompartment = 1,
+ kLocationOutsideTrain = 2
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Car
+//////////////////////////////////////////////////////////////////////////
+enum CarIndex {
+ kCarNone = 0,
+ kCarBaggageRear = 1,
+ kCarKronos = 2,
+ kCarGreenSleeping = 3,
+ kCarRedSleeping = 4,
+ kCarRestaurant = 5,
+ kCarBaggage = 6,
+ kCarCoalTender = 7,
+ kCarLocomotive = 8,
+ kCar9 = 9
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Clothes
+//////////////////////////////////////////////////////////////////////////
+enum ClothesIndex {
+ kClothesDefault = 0,
+ kClothes1 = 1,
+ kClothes2 = 2,
+ kClothes3 = 3,
+
+ kClothesInvalid
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Location of objects
+//////////////////////////////////////////////////////////////////////////
+enum ObjectLocation {
+ kObjectLocationNone = 0,
+ kObjectLocation1 = 1, // Floor?
+ kObjectLocation2 = 2, // Bed ?
+ kObjectLocation3 = 3,
+ kObjectLocation4 = 4, // Window ?
+ kObjectLocation5 = 5,
+ kObjectLocation6 = 6,
+ kObjectLocation7 = 7,
+ kObjectLocation8 = 8,
+ kObjectLocation9 = 9,
+ kObjectLocation10 = 10,
+ kObjectLocation18 = 18
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity direction
+//////////////////////////////////////////////////////////////////////////
+enum EntityDirection {
+ kDirectionNone = 0,
+ kDirectionUp = 1,
+ kDirectionDown = 2,
+ kDirectionLeft = 3,
+ kDirectionRight = 4,
+ kDirectionSwitch = 5
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Combat
+//////////////////////////////////////////////////////////////////////////
+enum FightType {
+ kFightMilos = 2001,
+ kFightAnna = 2002,
+ kFightIvo = 2003,
+ kFightSalko = 2004,
+ kFightVesna = 2005
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Index of items in inventory data
+//////////////////////////////////////////////////////////////////////////
+enum InventoryItem {
+ kItemNone,
+ kItemMatchBox,
+ kItem2,
+ kItem3,
+ kItemTelegram,
+ kItem5, // 5
+ kItemPassengerList,
+ kItem7,
+ kItemScarf,
+ kItem9,
+ kItemParchemin, // 10
+ kItem11,
+ kItemMatch,
+ kItemWhistle,
+ kItemBeetle,
+ kItemKey, // 15
+ kItemBomb,
+ kItem17,
+ kItemFirebird,
+ kItemBriefcase,
+ kItemCorpse, // 20
+ kItemGreenJacket,
+ kItem22,
+ kItemPaper,
+ kItemArticle,
+ kItem25, // 25
+ kItem26,
+ kItem27,
+ kItem28,
+ kItem29,
+ kItem30, // 30
+ kItem31,
+
+ // Portrait (not an index)
+ kPortraitOriginal = 32,
+ kPortraitGreen = 34,
+ kPortraitYellow = 36,
+
+ kItemInvalid = 128,
+
+ kItem146 = 146,
+ kItem147 = 147,
+
+ // Toggles
+ kItemToggleHigh = 0x7F,
+ kItemToggleLow = 0xF7
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Object ID
+//////////////////////////////////////////////////////////////////////////
+enum ObjectIndex {
+ kObjectNone,
+ kObjectCompartment1,
+ kObjectCompartment2,
+ kObjectCompartment3,
+ kObjectCompartment4,
+ kObjectCompartment5, // 5
+ kObjectCompartment6,
+ kObjectCompartment7,
+ kObjectCompartment8,
+ kObjectOutsideTylerCompartment,
+ kObject10, // 10
+ kObject11,
+ kObject12,
+ kObject13,
+ kObject14,
+ kObject15, // 15
+ kObject16,
+ kObjectHandleBathroom,
+ kObjectHandleInsideBathroom,
+ kObjectKitchen,
+ kObject20, // 20
+ kObject21,
+ kObject22,
+ kObjectTrainTimeTable,
+ kObjectRedSleepingCar,
+ kObject25, // 25
+ kObjectHandleOutsideLeft,
+ kObjectHandleOutsideRight,
+ kObject28,
+ kObject29,
+ kObject30, // 30
+ kObject31,
+ kObjectCompartmentA,
+ kObjectCompartmentB,
+ kObjectCompartmentC,
+ kObjectCompartmentD, // 35
+ kObjectCompartmentE,
+ kObjectCompartmentF,
+ kObjectCompartmentG,
+ kObjectCompartmentH,
+ kObject40, // 40
+ kObject41,
+ kObject42,
+ kObject43,
+ kObjectOutsideBetweenCompartments,
+ kObjectOutsideAnnaCompartment, // 45
+ kObject46,
+ kObject47,
+ kObject48, // might be the egg
+ kObject49,
+ kObject50, // 50
+ kObject51,
+ kObject52,
+ kObject53,
+ kObject54,
+ kObjectRestaurantCar, // 55
+ kObject56,
+ kObject57,
+ kObject58,
+ kObject59,
+ kObject60, // 60
+ kObject61,
+ kObject62,
+ kObject63,
+ kObject64,
+ kObject65, // 65
+ kObject66,
+ kObject67,
+ kObject68,
+ kObject69,
+ kObject70, // 70
+ kObject71,
+ kObject72,
+ kObjectCeiling,
+ kObject74,
+ kObjectCompartmentKronos, // 75
+ kObject76,
+ kObject77,
+ kObject78,
+ kObject79,
+ kObject80, // 80
+ kObject81,
+ kObject82,
+ kObject83,
+ kObject84,
+ kObject85, // 85
+ kObject86,
+ kObject87,
+ kObject88,
+ kObject89,
+ kObject90, // 90
+ kObject91,
+ kObject92,
+ kObject93,
+ kObject94,
+ kObject95, // 95
+ kObject96,
+ kObject97,
+ kObject98,
+ kObject99,
+ kObject100, // 100
+ kObject101,
+ kObject102,
+ kObject103,
+ kObject104,
+ kObject105, // 105
+ kObject106,
+ kObject107,
+ kObject108,
+ kObjectCageMax,
+ kObject110, // 110
+ kObject111,
+ kObject112,
+ kObject113,
+ kObject114,
+ kObject115, // 115
+ kObject116,
+ kObject117,
+ kObject118,
+ kObject119,
+ kObject120, // 120
+ kObject121,
+ kObject122,
+ kObject123,
+ kObject124,
+ kObject125, // 125
+ kObject126,
+ kObject127,
+ kObjectMax
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Entity ID
+//////////////////////////////////////////////////////////////////////////
+enum EntityIndex {
+ kEntityPlayer,
+ kEntityAnna,
+ kEntityAugust,
+ kEntityMertens,
+ kEntityCoudert,
+ kEntityPascale, // 5
+ kEntityServers0,
+ kEntityServers1,
+ kEntityCooks,
+ kEntityVerges,
+ kEntityTatiana, // 10
+ kEntityVassili,
+ kEntityAlexei,
+ kEntityAbbot,
+ kEntityMilos,
+ kEntityVesna, // 15
+ kEntityIvo,
+ kEntitySalko,
+ kEntityKronos,
+ kEntityKahina,
+ kEntityFrancois, // 20
+ kEntityMmeBoutarel,
+ kEntityBoutarel,
+ kEntityRebecca,
+ kEntitySophie,
+ kEntityMahmud, // 25
+ kEntityYasmin,
+ kEntityHadija,
+ kEntityAlouan,
+ kEntityGendarmes,
+ kEntityMax, // 30
+ kEntityChapters,
+ kEntityTrain,
+ kEntityTables0,
+ kEntityTables1,
+ kEntityTables2, // 35
+ kEntityTables3,
+ kEntityTables4,
+ kEntityTables5,
+ kEntity39,
+
+ kEntitySteam = 255
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Events
+// - a single D at the end means that Cath is on the right of the "scene" (D = Down the train, U = Up the train)
+// - DD: during the day, coming down the train
+// - DU: during the day, coming up the train
+// - ND: during the night, coming down the train
+// - NU: during the night, coming up the train
+//////////////////////////////////////////////////////////////////////////
+enum EventIndex {
+ kEventNone = 0,
+ kEventGotALight = 1,
+ kEventGotALightD = 2,
+ kEventDinerMindJoin = 3,
+ kEventDinerAugustOriginalJacket = 4,
+ kEventDinerAugust = 5,
+ kEventDinerAugustAlexeiBackground = 6,
+ kEventMeetAugustTylerCompartment = 7,
+ kEventMeetAugustTylerCompartmentBed = 8,
+ kEventMeetAugustHisCompartment = 9,
+ kEventMeetAugustHisCompartmentBed = 10,
+ kEventAugustFindCorpse = 11,
+ kEventAugustPresentAnna = 12,
+ kEventAugustPresentAnnaFirstIntroduction = 13,
+ kEventAnnaIntroductionRejected = 14,
+ kEventAnnaConversationGoodNight = 15,
+ kEventAnnaVisitToCompartmentGun = 16,
+ kEventInvalid_17 = 17,
+ kEventAnnaGoodNight = 18,
+ kEventAnnaGoodNightInverse = 19,
+ kEventAugustGoodMorning = 20,
+ kEventAugustMerchandise = 21,
+ kEventAugustTalkGold = 22,
+ kEventAugustTalkGoldDay = 23,
+ kEventAugustTalkCompartmentDoor = 24,
+ kEventAugustTalkCompartmentDoorBlueRedingote = 25,
+ kEventAugustLunch = 26,
+ kEventKronosVisit = 27,
+ kEventAnnaSearchingCompartment = 28,
+ kEventAugustBringEgg = 29,
+ kEventAugustBringBriefcase = 30,
+ kEventAugustTalkCigar = 31,
+ kEventAnnaBaggageArgument = 32,
+ kEventAnnaBagagePart2 = 33,
+ kEventAnnaConversation_34 = 34,
+ kEventAugustDrink = 35,
+ kEventAnnaTired = 36,
+ kEventAnnaTiredKiss = 37,
+ kEventAnnaBaggageTies = 38,
+ kEventAnnaBaggageTies2 = 39,
+ kEventAnnaBaggageTies3 = 40,
+ kEventAnnaBaggageTies4 = 41,
+ kEventAugustUnhookCarsBetrayal = 42,
+ kEventAugustUnhookCars = 43,
+ kEventLocomotiveAnnaStopsTrain = 44,
+ kEventInvalid_45 = 45,
+ kEventTrainStopped = 46,
+ kEventAnnaKissTrainHijacked = 47,
+ kEventTrainHijacked = 48,
+ kEventAnnaKilled = 49,
+ kEventKronosGoingToInvitation = 50,
+ kEventKronosConversation = 51,
+ kEventKahinaAskSpeakFirebird = 52,
+ kEventKahinaAskSpeak = 53,
+ kEventKronosConversationFirebird = 54,
+ kEventKahinaGunYellow = 55,
+ kEventKahinaGunBlue = 56,
+ kEventKahinaGun = 57,
+ kEventKronosBringEggCeiling = 58,
+ kEventKronosBringEgg = 59,
+ kEventKronosBringNothing = 60,
+ kEventKronosReturnBriefcase = 61,
+ kEventKronosHostageAnna = 62,
+ kEventKronosGiveFirebird = 63,
+ kEventKahinaPunchBaggageCarEntrance = 64,
+ kEventKahinaPunchBlue = 65,
+ kEventKahinaPunchYellow = 66,
+ kEventKahinaPunchSalon = 67,
+ kEventKahinaPunchKitchen = 68,
+ kEventKahinaPunchBaggageCar = 69,
+ kEventKahinaPunchCar = 70,
+ kEventKahinaPunchSuite4 = 71,
+ kEventKahinaPunchRestaurant = 72,
+ kEventKronosHostageAnnaNoFirebird = 73,
+ kEventKahinaPunch = 74,
+ kEventKahinaWrongDoor = 75,
+ kEventAlexeiDiner = 76,
+ kEventAlexeiDinerOriginalJacket = 77,
+ kEventAlexeiSalonVassili = 78,
+ kEventAlexeiSalonCath = 79,
+ kEventAlexeiSalonPoem = 80,
+ kEventTatianaAskMatchSpeakRussian = 81,
+ kEventTatianaAskMatch = 82,
+ kEventTatianaGivePoem = 83,
+ kEventVassiliSeizure = 84,
+ kEventTatianaBreakfastAlexei = 85,
+ kEventTatianaBreakfast = 86,
+ kEventTatianaBreakfastGivePoem = 87,
+ kEventTatianaAlexei = 88,
+ kEventTatianaCompartmentStealEgg = 89,
+ kEventTatianaCompartment = 90,
+ kEventVassiliCompartmentStealEgg = 91,
+ kEventTatianaTylerCompartment = 92,
+ kEventTylerCastleDream= 93,
+ kEventVassiliDeadAlexei = 94,
+ kEventCathFreePassengers = 95,
+ kEventTatianaVassiliTalk = 96,
+ kEventTatianaVassiliTalkNight = 97,
+ kEventMilosTylerCompartmentVisit = 98,
+ kEventMilosTylerCompartmentBedVisit = 99,
+ kEventMilosTylerCompartment = 100,
+ kEventMilosTylerCompartmentBed = 101,
+ kEventMilosTylerCompartmentDefeat = 102,
+ kEventMilosCorpseFloor = 103,
+ kEventMilosCompartmentVisitAugust = 104,
+ kEventMilosCorridorThanks = 105,
+ kEventMilosCorridorThanksD = 106,
+ kEventMilosCompartmentVisitTyler = 107,
+ kEventLocomotiveMilosDay = 108,
+ kEventLocomotiveMilosNight = 109,
+ kEventAbbotIntroduction = 110,
+ kEventAbbotWrongCompartment = 111,
+ kEventAbbotWrongCompartmentBed = 112,
+ kEventAbbotInvitationDrink = 113,
+ kEventAbbotDrinkGiveDetonator = 114,
+ kEventTrainExplosionBridge = 115,
+ kEventDefuseBomb = 116,
+ kEventAbbotDrinkDefuse = 117,
+ kEventMertensLastCar = 118,
+ kEventMertensLastCarOriginalJacket = 119,
+ kEventMertensKronosInvitation = 120,
+ kEventMertensKronosInvitationCompartment = 121,
+ kEventMertensKronosInvitationClosedWindows = 122,
+ kEventMertensBloodJacket = 123,
+ kEventCoudertBloodJacket = 124,
+ kEventMertensCorpseFloor = 125,
+ kEventMertensCorpseBed = 126,
+ kEventMertensDontMakeBed = 127,
+ kEventInvalid_128 = 128,
+ kEventGendarmesArrestation = 129,
+ kEventVergesSuitcase = 130,
+ kEventVergesSuitcaseStart = 131,
+ kEventVergesSuitcaseOtherEntry = 132,
+ kEventVergesSuitcaseOtherEntryStart = 133,
+ kEventVergesSuitcaseNight = 134,
+ kEventVergesSuitcaseNightStart = 135,
+ kEventVergesSuitcaseNightOtherEntry = 136,
+ kEventVergesSuitcaseNightOtherEntryStart = 137,
+ kEventMertensAskTylerCompartment = 138,
+ kEventMertensAskTylerCompartmentD = 139,
+ kEventMertensPushCall = 140,
+ kEventMertensPushCallNight = 141,
+ kEventMertensAugustWaiting = 142,
+ kEventMertensAugustWaitingCompartment = 143,
+ kEventIntroBroderbrund = 144,
+ kEventCoudertAskTylerCompartment = 145,
+ kEventMertensKronosConcertInvitation = 146,
+ kEventCoudertGoingOutOfVassiliCompartment = 147,
+ kEventLocomotiveConductorsDiscovered = 148,
+ kEventLocomotiveConductorsLook = 149,
+ kEventMahmudWrongDoor = 150,
+ kEventMahmudWrongDoorOriginalJacket = 151,
+ kEventMahmudWrongDoorDay = 152,
+ kEventVergesEscortToDiningCar = 153,
+ kEventVergesBaggageCarOffLimits = 154,
+ kEventVergesCanIHelpYou = 155,
+ kEventCoudertBaggageCar = 156,
+ kEventCathTurningDay = 157,
+ kEventCathTurningNight = 158,
+ kEventIntro = 159,
+ kEventCathDream = 160,
+ kEventCorpseDropBridge = 161,
+ kEventTrainPassing = 162,
+ kEventVergesAnnaDead = 163,
+ kEventViennaAugustUnloadGuns = 164,
+ kEventViennaKronosFirebird = 165,
+ kEventViennaContinueGame = 166,
+ kEventCathVesnaRestaurantKilled = 167,
+ kEventCathMaxCage = 168,
+ kEventCathMaxFree = 169,
+ kEventCathMaxLickHand = 170,
+ kEventCathIvoFight = 171,
+ kEventCathSalkoTrainTopFight = 172,
+ kEventCathVesnaTrainTopFight = 173,
+ kEventCathVesnaTrainTopKilled = 174,
+ kEventCathVesnaTrainTopWin = 175,
+ kEventCathSalkoTrainTopWin = 176,
+ kEventFrancoisWhistle = 177,
+ kEventFrancoisWhistleD = 178,
+ kEventFrancoisWhistleNight = 179,
+ kEventFrancoisWhistleNightD = 180,
+ kEventFrancoisShowBeetle = 181,
+ kEventFrancoisShowBeetleD = 182,
+ kEventFrancoisTradeWhistle = 183,
+ kEventFrancoisTradeWhistleD = 184,
+ kEventFrancoisShowEgg = 185,
+ kEventFrancoisShowEggD = 186,
+ kEventFrancoisShowEggNight = 187,
+ kEventFrancoisShowEggNightD = 188,
+ kEventKronosBringFirebird = 189,
+ kEventKronosOpenFirebird = 190,
+ kEventFinalSequence = 191,
+ kEventLocomotiveRestartTrain = 192,
+ kEventLocomotiveOldBridge = 193,
+ kEventLocomotiveAbbotGetSomeRest = 194,
+ kEventLocomotiveAbbotShoveling = 195,
+ kEventLocomotiveMilosShovelingDay = 196,
+ kEventLocomotiveMilosShovelingNight = 197,
+ kEventAnnaGiveScarf = 198,
+ kEventAnnaGiveScarfDiner = 199,
+ kEventAnnaGiveScarfSalon = 200,
+ kEventAnnaGiveScarfMonogram = 201,
+ kEventAnnaGiveScarfDinerMonogram = 202,
+ kEventAnnaGiveScarfSalonMonogram = 203,
+ kEventAnnaGiveScarfAsk = 204,
+ kEventAnnaGiveScarfDinerAsk = 205,
+ kEventAnnaGiveScarfSalonAsk = 206,
+ kEventAugustArrivalInMunich = 207,
+ kEventAnnaDialogGoToJerusalem = 208,
+ kEventConcertStart = 209,
+ kEventConcertEnd = 210,
+ kEventCathFallingAsleep = 211,
+ kEventCathWakingUp = 212,
+ kEventConcertCough = 213,
+ kEventConcertSit = 214,
+ kEventConcertLeaveWithBriefcase = 215,
+ kEventCorpseDropFloorOriginal = 216,
+ kEventCorpseDropFloorGreen = 217,
+ kEventCorpsePickFloorOriginal = 218,
+ kEventCorpsePickFloorGreen = 219,
+ kEventCorpsePickFloorOpenedBedOriginal = 220,
+ kEventCorpsePickBedOriginal = 221,
+ kEventCorpsePickBedGreen = 222,
+ kEventCorpseDropBedOriginal = 223,
+ kEventCorpseDropBedGreen = 224,
+ kEventCorpseDropWindowOriginal = 225,
+ kEventCorpseDropWindowGreen = 226,
+ kEventCathFindCorpse = 227,
+ kEventCathLookOutsideWindowDay = 228,
+ kEventCathLookOutsideWindowNight = 229,
+ kEventCathGoOutsideTylerCompartmentDay = 230,
+ kEventCathGoOutsideTylerCompartmentNight = 231,
+ kEventCathGoOutsideDay = 232,
+ kEventCathGoOutsideNight = 233,
+ kEventCathSlipTylerCompartmentDay = 234,
+ kEventCathSlipTylerCompartmentNight = 235,
+ kEventCathSlipDay = 236,
+ kEventCathSlipNight = 237,
+ kEventCathGetInsideTylerCompartmentDay = 238,
+ kEventCathGetInsideTylerCompartmentNight = 239,
+ kEventCathGetInsideDay = 240,
+ kEventCathGetInsideNight = 241,
+ kEventCathGettingInsideAnnaCompartment = 242,
+ kEventCathClimbUpTrainGreenJacket = 243,
+ kEventCathClimbUpTrainNoJacketNight = 244,
+ kEventCathClimbUpTrainNoJacketDay = 245,
+ kEventCathClimbDownTrainGreenJacket = 246,
+ kEventCathClimbDownTrainNoJacketNight = 247,
+ kEventCathClimbDownTrainNoJacketDay= 248,
+ kEventCathTopTrainGreenJacket = 249,
+ kEventCathTopTrainNoJacketNight = 250,
+ kEventCathTopTrainNoJacketDay = 251,
+ kEventCathBreakCeiling = 252,
+ kEventCathJumpDownCeiling = 253,
+ kEventCathJumpUpCeilingBriefcase = 254,
+ kEventCathJumpUpCeiling = 255,
+ kEventPickGreenJacket = 256,
+ kEventPickScarfGreen = 257,
+ kEventPickScarfOriginal = 258,
+ kEventCloseMatchbox = 259,
+ kEventCathStruggleWithBonds = 260,
+ kEventCathBurnRope = 261,
+ kEventCathRemoveBonds = 262,
+ kEventCathStruggleWithBonds2 = 263,
+ kEventCathDefusingBomb = 264,
+ kEventCathSmokeNight = 265,
+ kEventCathSmokeDay = 266,
+ kEventCathOpenEgg = 267,
+ kEventCathOpenEggNoBackground = 268,
+ kEventCathCloseEgg = 269,
+ kEventCathCloseEggNoBackground = 270,
+ kEventCathUseWhistleOpenEgg = 271,
+ kEventCathUseWhistleOpenEggNoBackground = 272
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Action ID (used by entity logic)
+//////////////////////////////////////////////////////////////////////////
+enum ActionIndex {
+ kActionNone = 0,
+ kAction1 = 1,
+ kActionEndSound = 2,
+ kActionExitCompartment = 3,
+ kAction4 = 4,
+ kActionExcuseMeCath = 5,
+ kActionExcuseMe = 6,
+ kActionKnock = 8,
+ kActionOpenDoor = 9,
+ kAction10 = 10,
+ kAction11 = 11,
+ kActionDefault = 12,
+ kAction16 = 16,
+ kActionDrawScene = 17,
+ kActionCallback = 18,
+
+ /////////////////////////////
+ // Abbot
+ /////////////////////////////
+ kAction100969180 = 100969180, // Anna
+ kAction101169422 = 101169422,
+ kAction104060776 = 104060776,
+ kAction135600432 = 135600432,
+ kAction136196244 = 136196244,
+ kAction157159392 = 157159392,
+ kAction157489665 = 157489665,
+ kAction158480160 = 158480160,
+ kAction192054567 = 192054567,
+ kAction203073664 = 203073664,
+ kAction222609266 = 222609266,
+
+ /////////////////////////////
+ // Alexei
+ /////////////////////////////
+ kAction100906246 = 100906246,
+ kAction123536024 = 123536024,
+ kAction124697504 = 124697504,
+ kAction135664192 = 135664192,
+ kAction135854208 = 135854208,
+ kAction188784532 = 188784532,
+ kAction221617184 = 221617184,
+
+ /////////////////////////////
+ // Alouan
+ /////////////////////////////
+ kAction189489753 = 189489753,
+ kAction190219584 = 190219584, // Francois
+
+ /////////////////////////////
+ // Anna
+ /////////////////////////////
+ kAction136702400 = 136702400,
+ kAction139254416 = 139254416,
+ kAction156049968 = 156049968,
+ kAction157370960 = 157370960,
+ kAction157894320 = 157894320,
+ kAction159332865 = 159332865, // August
+ kAction189299008 = 189299008,
+ kAction191668032 = 191668032, // some action during or before concert?
+ kAction201437056 = 201437056,
+ kAction235856512 = 235856512,
+ kAction236060709 = 236060709,
+ kAction238936000 = 238936000,
+ kAction259136835 = 259136835,
+ kAction291662081 = 291662081,
+
+
+ /////////////////////////////
+ // August
+ /////////////////////////////
+ kAction123793792 = 123793792,
+ kAction134611040 = 134611040,
+ kAction168046720 = 168046720,
+ kAction168627977 = 168627977,
+ kAction169032608 = 169032608,
+ kAction189426612 = 189426612,
+ kAction203859488 = 203859488,
+ kAction219522616 = 219522616, // Servers0
+ kAction225182640 = 225182640,
+ kAction235257824 = 235257824,
+
+ /////////////////////////////
+ // Boutarel
+ /////////////////////////////
+ kAction125039808 = 125039808,
+ kAction134466544 = 134466544,
+ kAction135854206 = 135854206,
+ kAction159003408 = 159003408,
+ kAction203520448 = 203520448,
+ kAction237889408 = 237889408,
+
+ /////////////////////////////
+ // Chapters
+ /////////////////////////////
+ kAction135800432 = 135800432,
+ kActionChapter3 = 139122728,
+ kActionChapter5 = 139254416,
+ kAction156435676 = 156435676,
+ kAction169629818 = 169629818,
+ kAction171843264 = 171843264,
+ kAction190346110 = 190346110,
+
+ /////////////////////////////
+ // Cooks
+ /////////////////////////////
+ kAction101632192 = 101632192,
+ kAction224849280 = 224849280,
+ kAction236976550 = 236976550,
+
+ /////////////////////////////
+ // Coudert
+ /////////////////////////////
+ kAction123733488 = 123733488,
+ kAction154005632 = 154005632,
+ kAction155991520 = 155991520,
+ kAction157026693 = 157026693,
+ kAction168253822 = 168253822,
+ kAction168254872 = 168254872,
+ kAction168316032 = 168316032, // Tatiana
+ kAction169557824 = 169557824,
+ kAction171394341 = 171394341, // Mertens
+ kAction185671840 = 185671840,
+ kAction185737168 = 185737168,
+ kAction188570113 = 188570113,
+ kAction189026624 = 189026624,
+ kAction189750912 = 189750912,
+ kAction192063264 = 192063264, // Anna
+ kAction201431954 = 201431954, // Mertens / Verges
+ kAction201439712 = 201439712,
+ kAction205033696 = 205033696,
+ kAction205346192 = 205346192, // Francois
+ kAction219971920 = 219971920, // Anna
+ kAction223068211 = 223068211, // MmeBoutarel
+ kAction225932896 = 225932896,
+ kAction226031488 = 226031488, // Verges
+ kAction235061888 = 235061888, // Tatiana
+ kAction238358920 = 238358920, // Anna
+ kAction253868128 = 253868128, // Anna
+ kAction285528346 = 285528346, // Rebecca
+ kAction292048641 = 292048641,
+ kAction305159806 = 305159806,
+ kAction326348944 = 326348944,
+ kAction339669520 = 339669520, // Verges
+
+ /////////////////////////////
+ // Francois
+ /////////////////////////////
+ kAction100901266 = 100901266,
+ kAction100957716 = 100957716,
+ kAction101107728 = 101107728,
+ kAction189872836 = 189872836,
+ kAction190390860 = 190390860,
+
+ /////////////////////////////
+ // Gendarmes
+ /////////////////////////////
+ kAction168710784 = 168710784,
+ kAction169499649 = 169499649,
+
+ /////////////////////////////
+ // Kahina
+ /////////////////////////////
+ kAction92186062 = 92186062,
+ kAction137503360 = 137503360,
+ kAction237555748 = 237555748,
+
+ /////////////////////////////
+ // Kronos
+ /////////////////////////////
+ kAction137685712 = 137685712,
+ kAction138085344 = 138085344,
+ kAction171849314 = 171849314,
+ kAction235599361 = 235599361,
+
+ /////////////////////////////
+ // Mahmud
+ /////////////////////////////
+ kAction102227384 = 102227384, // Mertens
+ kAction156567128 = 156567128,
+ kAction170483072 = 170483072,
+ kAction225563840 = 225563840,
+
+ /////////////////////////////
+ // Max
+ /////////////////////////////
+ kAction71277948 = 71277948,
+ kAction158007856 = 158007856,
+ kAction101687594 = 101687594,
+ kAction122358304 = 122358304, // also Servers1/Boutarel?
+ kActionMaxFreeFromCage = 135204609,
+ kAction156622016 = 156622016,
+
+ /////////////////////////////
+ // Mertens
+ /////////////////////////////
+ kAction155604840 = 155604840, // MmeBoutarel
+ kAction169633856 = 169633856,
+ kAction188635520 = 188635520,
+ kAction190082817 = 190082817,
+ kAction192849856 = 192849856,
+ kAction204379649 = 204379649,
+ kAction224122407 = 224122407,
+ kAction238732837 = 238732837,
+ kAction238790488 = 238790488, // Tatiana
+ kAction269436673 = 269436673,
+ kAction269624833 = 269624833,
+ kAction302614416 = 302614416,
+ kAction303343617 = 303343617,
+
+ /////////////////////////////
+ // Milos
+ /////////////////////////////
+ kAction88652208 = 88652208, // Coudert
+ kAction122865568 = 122865568,
+ kAction123852928 = 123852928,
+ kAction123199584 = 123199584, // Coudert
+ kAction157691176 = 157691176,
+ kAction208228224 = 208228224,
+ kAction221683008 = 221683008,
+ kAction259125998 = 259125998,
+
+ /////////////////////////////
+ // Mme Boutarel
+ /////////////////////////////
+ kAction102484312 = 102484312,
+ kAction102752636 = 102752636,
+ kAction134289824 = 134289824,
+ kAction168986720 = 168986720,
+ kAction202221040 = 202221040,
+ kAction242526416 = 242526416,
+
+ /////////////////////////////
+ // Pascale
+ /////////////////////////////
+ kAction101824388 = 101824388,
+ kAction136059947 = 136059947,
+ kAction169750080 = 169750080,
+ kAction190605184 = 190605184,
+ kAction191604416 = 191604416,
+ kAction207769280 = 207769280,
+ kAction223262556 = 223262556,
+ kAction239072064 = 239072064,
+ kAction257489762 = 257489762,
+ kAction269479296 = 269479296,
+ kAction352703104 = 352703104,
+ kAction352768896 = 352768896,
+
+ /////////////////////////////
+ // Rebecca
+ /////////////////////////////
+ kAction125496184 = 125496184,
+ kAction155465152 = 155465152,
+ kAction155980128 = 155980128,
+ kAction169358379 = 169358379,
+ kAction224253538 = 224253538,
+ kAction254915200 = 254915200,
+
+ /////////////////////////////
+ // Salko
+ /////////////////////////////
+ kAction55996766 = 55996766,
+ kAction101169464 = 101169464,
+ kAction102675536 = 102675536, // Ivo
+ kAction136184016 = 136184016,
+
+ /////////////////////////////
+ // Servers 0
+ /////////////////////////////
+ kAction170016384 = 170016384,
+ kAction188893625 = 188893625,
+ kAction201964801 = 201964801, // August
+ kAction204704037 = 204704037,
+ kAction207330561 = 207330561,
+ kAction218128129 = 218128129,
+ kAction218586752 = 218586752,
+ kAction218983616 = 218983616,
+ kAction223712416 = 223712416,
+ kAction237485916 = 237485916,
+ kAction252568704 = 252568704,
+ kAction268773672 = 268773672, // Anna / August
+ kAction270068760 = 270068760,
+ kAction270410280 = 270410280,
+ kAction286403504 = 286403504,
+ kAction286534136 = 286534136,
+ kAction292758554 = 292758554,
+ kAction304061224 = 304061224,
+ kAction337548856 = 337548856,
+
+ /////////////////////////////
+ // Servers 1
+ /////////////////////////////
+ kAction101106391 = 101106391,
+ kAction122288808 = 122288808, // Boutarel
+ kAction123712592 = 123712592, // Ivo
+ kAction125826561 = 125826561, // August
+ kAction134486752 = 134486752, // August
+ kAction168717392 = 168717392, // Boutarel
+ kAction189688608 = 189688608,
+ kAction219377792 = 219377792,
+ kAction223002560 = 223002560,
+ kAction236237423 = 236237423,
+ kAction256200848 = 256200848,
+ kAction258136010 = 258136010,
+ kAction269485588 = 269485588,
+ kAction291721418 = 291721418,
+ kAction302203328 = 302203328,
+ kAction302996448 = 302996448,
+ kAction326144276 = 326144276,
+
+ /////////////////////////////
+ // Sophie
+ /////////////////////////////
+ kActionProceedChapter5 = 70549068,
+ kAction123668192 = 123668192,
+ kAction125242096 = 125242096,
+ kAction136654208 = 136654208,
+ kAction259921280 = 259921280,
+ kAction292775040 = 292775040,
+
+ /////////////////////////////
+ // Tables
+ /////////////////////////////
+ kActionDrawTablesWithChairs = 103798704,
+ kAction136455232 = 136455232,
+
+ /////////////////////////////
+ // Tatiana
+ /////////////////////////////
+ kAction69239528 = 69239528,
+ kAction123857088 = 123857088,
+ kAction124973510 = 124973510,
+ kAction154071333 = 154071333,
+ kAction156444784 = 156444784,
+ kAction169360385 = 169360385,
+ kAction191198209 = 191198209,
+ kAction223183000 = 223183000, // August
+ kAction236053296 = 236053296, // Alexei
+ kAction236241630 = 236241630, // Anna
+ kAction236517970 = 236517970, // Anna
+ kAction268620864 = 268620864, // August
+ kAction290869168 = 290869168,
+
+ /////////////////////////////
+ // Train
+ /////////////////////////////
+ kAction191070912 = 191070912,
+ kActionTrainStopRunning = 191350523,
+ kActionCatchBeetle = 202613084,
+ kAction203339360 = 203339360,
+ kActionTrainStartRunning = 203419131,
+ kAction203863200 = 203863200,
+ kAction222746496 = 222746496,
+ kActionBreakCeiling = 225056224,
+ kAction290410610 = 290410610,
+ kActionJumpDownCeiling = 338494260,
+
+ /////////////////////////////
+ // Verges
+ /////////////////////////////
+ kAction125233040 = 125233040, // Abbot
+ kAction125499160 = 125499160,
+ kAction155853632 = 155853632,
+ kAction158617345 = 158617345,
+ kAction167854368 = 167854368,
+ kAction168187490 = 168187490,
+ kAction168255788 = 168255788,
+ kActionDeliverMessageToTyler = 191337656,
+ kAction202558662 = 202558662,
+
+ /////////////////////////////
+ // Vassili
+ /////////////////////////////
+ kAction122732000 = 122732000,
+ kAction168459827 = 168459827,
+ kAction191477936 = 191477936,
+
+ /////////////////////////////
+ // Vesna
+ /////////////////////////////
+ kAction124190740 = 124190740,
+ kAction134427424 = 134427424,
+ kAction135024800 = 135024800,
+ kAction137165825 = 137165825,
+ kAction155913424 = 155913424,
+ kAction190412928 = 190412928,
+ kAction203663744 = 203663744,
+ kAction204832737 = 204832737,
+
+ /////////////////////////////
+ // Misc
+ /////////////////////////////
+ kAction158610240 = 158610240,
+ kAction167992577 = 167992577,
+ kAction168646401 = 168646401,
+ kAction169300225 = 169300225,
+ kAction169773228 = 169773228,
+ kActionEndChapter = 190346110,
+ kAction191001984 = 191001984,
+ kAction192637492 = 192637492,
+ kAction201959744 = 201959744,
+ kAction202621266 = 202621266,
+ kAction202884544 = 202884544,
+ kAction203078272 = 203078272,
+ kAction205034665 = 205034665,
+ kAction205294778 = 205294778,
+ kActionUseWhistle = 270751616,
+ kAction272177921 = 272177921,
+ kAction224309120 = 224309120,
+ kAction225358684 = 225358684,
+ kAction225367984 = 225367984,
+ kAction226078300 = 226078300, // Whistle
+
+ kActionEnd
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Functors classes used by the engine
+//////////////////////////////////////////////////////////////////////////
+
+// FIXME is this achievable with the existing Functor1Mem function
+template<class Arg, class Res, class T>
+class Functor1MemConst : public Common::Functor1<Arg, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg) const;
+
+ Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg v1) const {
+ return (_t->*_func)(v1);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+// FIXME move this to existing func.h file
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Result>
+struct QuaternaryFunction {
+ typedef Arg1 FirstArgumentType;
+ typedef Arg2 SecondArgumentType;
+ typedef Arg3 ThirdArgumentType;
+ typedef Arg4 FourthArgumentType;
+ typedef Result ResultType;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res>
+struct Functor4 : public QuaternaryFunction<Arg1, Arg2, Arg3, Arg4, Res> {
+ virtual ~Functor4() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()(Arg1, Arg2, Arg3, Arg4) const = 0;
+};
+
+template<class Arg1, class Arg2, class Arg3, class Arg4, class Res, class T>
+class Functor4Mem : public Functor4<Arg1, Arg2, Arg3, Arg4, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg1, Arg2, Arg3, Arg4);
+
+ Functor4Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg1 v1, Arg2 v2, Arg3 v3, Arg4 v4) const {
+ return (_t->*_func)(v1, v2, v3, v4);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+
+} // End of namespace LastExpress
+
+#endif // LASTEXPRESS_SHARED_H
diff --git a/engines/lure/debugger.cpp b/engines/lure/debugger.cpp
index 1cfe0804e4..5fbe124919 100644
--- a/engines/lure/debugger.cpp
+++ b/engines/lure/debugger.cpp
@@ -323,7 +323,7 @@ bool Debugger::cmd_hotspot(int argc, const char **argv) {
if (h != NULL) {
DebugPrintf("Frame Number = %d of %d\n", h->frameNumber(), h->numFrames());
- DebugPrintf("Persistant = %s\n", h->persistant() ? "true" : "false");
+ DebugPrintf("Persistent = %s\n", h->persistant() ? "true" : "false");
}
} else if (strcmp(argv[2], "actions") == 0) {
diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp
index a84a84b51f..64e5035ee9 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);
}
}
@@ -4162,6 +4170,7 @@ PathFinderResult PathFinder::process() {
_inProgress = true;
initVars();
+ Common::Point diff(_destX - _xCurrent, _destY - _yCurrent);
_xCurrent >>= 3; _yCurrent >>= 3;
_xDestCurrent >>= 3; _yDestCurrent >>= 3;
if ((_xCurrent == _xDestCurrent) && (_yCurrent == _yDestCurrent)) {
@@ -4170,6 +4179,10 @@ PathFinderResult PathFinder::process() {
add(RIGHT, _xDestPos);
else if (_xDestPos < 0)
add(LEFT, -_xDestPos);
+ else if (diff.y > 0)
+ add(DOWN, diff.y);
+ else
+ add(UP, -diff.y);
_inProgress = false;
result = PF_OK;
@@ -4345,7 +4358,7 @@ PathFinderResult PathFinder::process() {
break;
}
- // Add a final move if necessary
+ // Add final movement if necessary
if (result == PF_OK) {
if (_xDestPos < 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/lure.h b/engines/lure/lure.h
index 15336a3507..297fb20f59 100644
--- a/engines/lure/lure.h
+++ b/engines/lure/lure.h
@@ -46,10 +46,10 @@
/**
* This is the namespace of the Lure engine.
*
- * Status of this engine: ???
+ * Status of this engine: Complete
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Lure of the Temptress
*/
namespace Lure {
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/res_struct.cpp b/engines/lure/res_struct.cpp
index a75ba6eac8..9ef595b30f 100644
--- a/engines/lure/res_struct.cpp
+++ b/engines/lure/res_struct.cpp
@@ -875,7 +875,7 @@ CharacterScheduleEntry::CharacterScheduleEntry(CharacterScheduleEntry *src) {
_parent = src->_parent;
_action = src->_action;
_numParams = src->_numParams;
- Common::copy(src->_params, src->_params + MAX_TELL_COMMANDS * 3 * sizeof(uint16), _params);
+ Common::copy(src->_params, src->_params + MAX_TELL_COMMANDS * 3, _params);
}
uint16 CharacterScheduleEntry::param(int index) {
diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp
index 20758466ad..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);
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/console.cpp b/engines/m4/console.cpp
index 71c70e3e1b..32ccdab787 100644
--- a/engines/m4/console.cpp
+++ b/engines/m4/console.cpp
@@ -61,8 +61,10 @@ static int strToInt(const char *s) {
return atoi(s);
// Hexadecimal string
- uint tmp;
- sscanf(s, "%xh", &tmp);
+ uint tmp = 0;
+ int read = sscanf(s, "%xh", &tmp);
+ if (read < 1)
+ error("strToInt failed on string \"%s\"", s);
return (int)tmp;
}
@@ -319,17 +321,25 @@ bool MadsConsole::cmdMessage(int argc, const char **argv) {
DebugPrintf("message 'objnum'\n");
} else if (!strcmp(argv[1], "list_quotes")) {
// Dump the quotes list
+#if 0
+ // FIXME: The following code is not portable and hence has been disabled.
+ // Try replacing FILE by Common::DumpFile.
FILE *destFile = fopen("mads_quotes.txt", "wb");
for (uint i = 0; i < _vm->globals()->getQuotesSize(); ++i)
fprintf(destFile, "%.3d - %s\n", i, _vm->globals()->getQuote(i));
fclose(destFile);
+#endif
} else if (!strcmp(argv[1], "list_vocab")) {
// Dump the vocab list
+#if 0
+ // FIXME: The following code is not portable and hence has been disabled.
+ // Try replacing FILE by Common::DumpFile.
FILE *destFile = fopen("mads_vocab.txt", "wb");
for (uint i = 1; i <= _vm->globals()->getVocabSize(); ++i)
fprintf(destFile, "%.3d/%.3x - %s\n", i, i, _vm->globals()->getVocab(i));
fclose(destFile);
+#endif
} else {
int messageIdx = strToInt(argv[1]);
diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp
index af26a86313..6a11c328ff 100644
--- a/engines/m4/converse.cpp
+++ b/engines/m4/converse.cpp
@@ -541,6 +541,9 @@ void Converse::loadConversation(const char *convName) {
if (debugFlag) printf("Reply data offset: %i\n", data);
}
+ if (!curEntry)
+ error("Converse::loadConversation(): curEntry is NULL while adding a reply");
+
curEntry->entries.push_back(replyEntry);
setEntryInfo(replyEntry->offset, replyEntry->entryType,
curNode, _convNodes[curNode]->entries.size() - 1);
@@ -572,6 +575,8 @@ void Converse::loadConversation(const char *convName) {
} else if (replyEntry != NULL && replyEntry->entryType == kWeightedReply) {
parentEntry = replyEntry->entries[currentWeightedEntry];
currentWeightedEntry++;
+ } else {
+ error("Converse::loadConversation(): Unexpected reply entry while processing TEXT/MESG chunk");
}
size = convS->readUint32LE();
diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp
index a7104537f5..101f21f48c 100644
--- a/engines/m4/dialogs.cpp
+++ b/engines/m4/dialogs.cpp
@@ -297,7 +297,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v
char *lineP = &dialogLine[0];
char *cmdP = NULL;
- while (*(srcP - 1) != '\0') {
+ while (srcP && *(srcP - 1) != '\0') {
if ((*srcP == '\n') || (*srcP == '\0')) {
// Line completed
*lineP = '\0';
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index a999a6bd5a..34fcef7c88 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -267,6 +267,9 @@ void MadsM4Engine::loadMenu(MenuType menuType, bool loadSaveFromHotkey, bool cal
}
void MadsM4Engine::dumpFile(const char* filename, bool uncompress) {
+#if 0
+ // FIXME: The following code is not portable and hence has been disabled.
+ // Try replacing FILE by Common::DumpFile.
Common::SeekableReadStream *fileS = res()->get(filename);
byte buffer[256];
FILE *destFile = fopen(filename, "wb");
@@ -295,6 +298,7 @@ void MadsM4Engine::dumpFile(const char* filename, bool uncompress) {
fclose(destFile);
res()->toss(filename);
res()->purge();
+#endif
}
/*--------------------------------------------------------------------------*/
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index b68f7248af..8dfae754ef 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -70,7 +70,7 @@
* functionality has been implemented. No further work has been done on this for some time, so progress
* on this part of the engine can be considered frozen.
*
- * Games this engine will support:
+ * Games using this engine:
* MADS Games: Dragonsphere, Return of the Phantom, Rex Nebular and the Cosmic Gender Bender
* M4 Games: Orion Burger, The Riddle of Master Lu
*/
@@ -161,7 +161,6 @@ public:
const char *getGameFile(int fileType);
Common::EventManager *eventMan() { return _eventMan; }
- OSystem *system() { return _system; }
const M4GameDescription *_gameDescription;
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index cebe2215ca..2d4a581abe 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -658,12 +658,12 @@ void MadsSceneLogic::execute(uint32 subOffset) {
case OP_NOT: // logical nots top item on stack
param = stack.pop().get();
- stack.push(ScriptVar((uint32)!param & 0xffff));
+ stack.push(ScriptVar((uint32)(!param) & 0xffff));
break;
case OP_COMP: // complements top item on stack
param = stack.pop().get();
- stack.push(ScriptVar(~param & 0xffff));
+ stack.push(ScriptVar((~param) & 0xffff));
break;
case OP_NEG: // negates top item on stack
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_views.cpp b/engines/m4/mads_views.cpp
index 58a9a99211..cc127032eb 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -397,6 +397,7 @@ int MadsSpriteSlots::addSprites(const char *resName, bool suppressErrors, int fl
// 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");
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index 94926014d3..51cdd83b2a 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -107,6 +107,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/made.h b/engines/made/made.h
index 08f9add33d..ed391562c3 100644
--- a/engines/made/made.h
+++ b/engines/made/made.h
@@ -53,8 +53,11 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Return to Zork
+ * - Leather Goddesses of Phobos 2
+ * - The Manhole
+ * - Rodney's Funscreen
*/
namespace Made {
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/made/screen.cpp b/engines/made/screen.cpp
index 81c18ef64b..773e3d17ed 100644
--- a/engines/made/screen.cpp
+++ b/engines/made/screen.cpp
@@ -204,7 +204,7 @@ void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 f
for (int16 yc = 0; yc < clipHeight; yc++) {
linePtr = source + sourceAdd;
for (int16 xc = 0; xc < clipWidth; xc++) {
- if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || maskp[xc] == 0))) {
+ if (*linePtr && (_vm->getGameID() == GID_RTZ || (mask == 0 || (maskp && maskp[xc] == 0)))) {
if (*linePtr)
dest[xc] = *linePtr;
}
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 0116167df1..3eed08e3c1 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -367,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;
}
@@ -393,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;
}
@@ -408,7 +398,6 @@ bool RivenConsole::Cmd_StopSound(int argc, const char **argv) {
_vm->_sound->stopSound();
_vm->_sound->stopAllSLST();
-
return true;
}
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index 64629f949d..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;
@@ -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
@@ -696,6 +707,22 @@ void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect
_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];
diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h
index 5271c86d0d..9419aad277 100644
--- a/engines/mohawk/graphics.h
+++ b/engines/mohawk/graphics.h
@@ -150,6 +150,7 @@ public:
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);
diff --git a/engines/mohawk/mohawk.h b/engines/mohawk/mohawk.h
index e4d26d16f7..7bd76193fc 100644
--- a/engines/mohawk/mohawk.h
+++ b/engines/mohawk/mohawk.h
@@ -37,6 +37,15 @@ namespace Common {
class SeekableReadStream;
}
+/**
+ * This is the namespace of the Mohawk engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Myst
+ * - Riven: The Sequel to Myst
+ */
namespace Mohawk {
enum MohawkGameType {
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 c7428a92fb..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"
@@ -251,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())
@@ -285,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
@@ -652,7 +652,7 @@ void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
}
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");
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 13818810f1..251a0fc753 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -141,7 +141,6 @@ private:
// Hotspot related functions and variables
uint16 _hotspotCount;
void loadHotspots(uint16);
- void checkHotspotChange();
void checkInventoryClick();
bool _showHotspots;
void updateZipMode();
@@ -176,6 +175,7 @@ public:
void runHotspotScript(uint16 hotspot, uint16 scriptType);
int32 getCurHotspot() const { return _curHotspot; }
Common::String getHotspotName(uint16 hotspot);
+ void checkHotspotChange();
// Variable functions
void initVars();
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 5a2321a07a..21464a6a48 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -277,7 +277,7 @@ void RivenExternal::resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 sta
// 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, false);
+ _vm->_sound->playSound(soundId);
drawDomeSliders(bitmapId, startHotspot);
_vm->_system->delayMillis(10);
}
@@ -350,7 +350,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
_sliderState |= 1 << (24 - foundSlider);
// Now play a click sound and redraw
- _vm->_sound->playSound(soundId, false);
+ _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
@@ -359,7 +359,7 @@ void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 reset
_sliderState |= 1 << (24 - foundSlider);
// Now play a click sound and redraw
- _vm->_sound->playSound(soundId, false);
+ _vm->_sound->playSound(soundId);
drawDomeSliders(bitmapId, startHotspot);
} else
_vm->_system->updateScreen(); // A normal update for the cursor
@@ -453,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);
@@ -473,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);
@@ -541,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);
@@ -558,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);
@@ -577,7 +577,7 @@ void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {
*_vm->getVar("atrap") = 0;
// Play the page turning sound
- _vm->_sound->playSound(8, false);
+ _vm->_sound->playSound(8);
_vm->refreshCard();
}
@@ -587,7 +587,7 @@ void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {
*_vm->getVar("atrap") = 1;
// Play the page turning sound
- _vm->_sound->playSound(9, false);
+ _vm->_sound->playSound(9);
_vm->refreshCard();
}
@@ -698,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);
@@ -715,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);
@@ -739,54 +739,60 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
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());
}
}
@@ -795,7 +801,8 @@ 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) {
@@ -804,18 +811,16 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
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) {
@@ -1050,7 +1055,9 @@ 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) {
@@ -1142,7 +1149,7 @@ void RivenExternal::xcheckicons(uint16 argc, uint16 *argv) {
if (countDepressedIcons(*iconOrderVar) == 5) {
*iconOrderVar = 0;
*_vm->getVar("jicons") = 0;
- _vm->_sound->playSound(46, false);
+ _vm->_sound->playSound(46);
}
}
@@ -1562,12 +1569,12 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
_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, false); // Play the link sound
+ _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, false); // Play the link sound again
+ *_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;
}
@@ -1654,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);
@@ -1671,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);
@@ -1697,7 +1704,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
if (curSound == 5) // Break out after the last sound is done
break;
- _vm->_sound->playSound(getComboDigit(*prisonCombo, curSound) + 13, !(_vm->getFeatures() & GF_DVD));
+ _vm->_sound->playSound(getComboDigit(*prisonCombo, curSound) + 13);
curSound++;
soundTime = _vm->_system->getMillis();
}
@@ -1724,7 +1731,7 @@ 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
@@ -1845,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
@@ -1870,7 +1879,9 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
// 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;
}
@@ -1917,28 +1928,209 @@ 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->getVar("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) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index da22e3bd81..1f012c82d9 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -45,6 +45,7 @@ public:
private:
MohawkEngine_Riven *_vm;
uint32 _sliderState;
+ Common::Array<Common::Rect> _marbleBaseHotspots;
typedef void (RivenExternal::*ExternalCmd)(uint16 argc, uint16 *argv);
@@ -69,6 +70,8 @@ private:
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
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index c055eb51a8..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;
}
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 2b91dce35a..30d1d727eb 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -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)
@@ -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]) {
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/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/gfxbase.cpp b/engines/parallaction/gfxbase.cpp
index a1926fc197..6c39b2e696 100644
--- a/engines/parallaction/gfxbase.cpp
+++ b/engines/parallaction/gfxbase.cpp
@@ -322,7 +322,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
uint line = 0, col = 0;
uint xAccum = 0, yAccum = 0;
- uint inc = width * (100 - scale);
+ uint inc = width * (100 - scale);
uint thr = width * 100;
for (uint16 i = 0; i < srcRect.height(); i++) {
diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp
index 562c806958..51d3ba5799 100644
--- a/engines/parallaction/gui_ns.cpp
+++ b/engines/parallaction/gui_ns.cpp
@@ -118,6 +118,7 @@ public:
ChooseLanguageInputState_NS(Parallaction *vm, MenuInputHelper *helper) : MenuInputState("chooselanguage", helper), _vm(vm) {
_allowChoice = false;
_nextState = "selectgame";
+ _label = 0;
_dosLanguageSelectBlocks[0] = Common::Rect( 80, 110, 128, 180 ); // Italian
_dosLanguageSelectBlocks[1] = Common::Rect( 129, 85, 177, 155 ); // French
@@ -147,7 +148,6 @@ public:
_blocks = _dosLanguageSelectBlocks;
}
- _label = 0;
_language = -1;
_allowChoice = true;
}
@@ -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/parallaction.h b/engines/parallaction/parallaction.h
index 7bbdf79f1c..ded1b3bda4 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -48,8 +48,9 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Nippon Safes Inc. (complete)
+ * - The Big Red Adventure (work in progress)
*/
namespace Parallaction {
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index 470c698a21..1cbd857ac9 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -154,7 +154,8 @@ Common::Error Parallaction_br::go() {
while (!shouldQuit()) {
if (getFeatures() & GF_DEMO) {
- scheduleLocationSwitch("camalb.1");
+ scheduleLocationSwitch("camalb");
+ _nextPart = 1;
_input->_inputMode = Input::kInputModeGame;
} else {
startGui(splash);
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/staticres.cpp b/engines/parallaction/staticres.cpp
index fa1ac910e7..84db3af9a4 100644
--- a/engines/parallaction/staticres.cpp
+++ b/engines/parallaction/staticres.cpp
@@ -54,216 +54,215 @@ byte Input::_resMouseArrow_NS[256] = {
TODO: The cursor data should be adjusted by adding 0x10 to each byte, because the bitmap
must be drawn using the background palette.
*/
-byte Input::_resMouseArrow_BR_Amiga[512] =
-{
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
- 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+byte Input::_resMouseArrow_BR_Amiga[512] = {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*
This palette snippet is used for animations in Big Red Adventure.
*/
byte _braAmigaFramesDefaultPalette[48] = {
- 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
- 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
- 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
+ 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
+ 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
+ 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
};
byte _amigaTopazFont[2600] = {
- 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79,
- 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x00, 0x1a, 0x0f, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x45, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x74, 0x00, 0x08,
- 0x00, 0x40, 0x00, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x00, 0x6e,
- 0x00, 0xbe, 0x00, 0x00, 0x06, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
- 0x6c, 0x6c, 0x18, 0x00, 0x38, 0x18, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, 0x18,
- 0x3c, 0x3c, 0x1c, 0x7e, 0x1c, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x3c,
- 0x7c, 0x1e, 0x78, 0x7e, 0x7e, 0x3c, 0x66, 0x3c, 0x06, 0xc6, 0x60, 0xc6, 0xc6, 0x3c, 0x7c, 0x78,
- 0x7c, 0x3c, 0x7e, 0x66, 0x66, 0xc6, 0xc3, 0xc3, 0xfe, 0x3c, 0xc0, 0x3c, 0x10, 0x00, 0x18, 0x00,
- 0x60, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x60, 0x18, 0x0c, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x70, 0x72, 0x0f, 0x00, 0x18,
- 0x00, 0x1c, 0x42, 0xc3, 0x18, 0x3c, 0x66, 0x7e, 0x1c, 0x00, 0x3e, 0x7e, 0x7e, 0x3c, 0x18, 0x78,
- 0x78, 0x18, 0x00, 0x3e, 0x00, 0x00, 0x30, 0x38, 0x00, 0x40, 0x40, 0xc0, 0x18, 0x3c, 0x3c, 0x7e,
- 0x06, 0x66, 0x18, 0x7e, 0x7e, 0x36, 0x0c, 0x0c, 0x18, 0x3c, 0xc6, 0x3c, 0x60, 0x76, 0x18, 0x00,
- 0x0c, 0x7e, 0x71, 0x66, 0x00, 0x66, 0x60, 0x0e, 0x7e, 0x66, 0x18, 0x6e, 0x3c, 0x00, 0x18, 0x7e,
- 0x06, 0x66, 0x18, 0x00, 0x7e, 0x34, 0x0c, 0x0c, 0x18, 0x0c, 0x60, 0x00, 0x18, 0x3c, 0x0c, 0x00,
- 0x0c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x7e, 0x00, 0x18, 0x3c, 0x00, 0x18, 0x6c, 0x6c,
- 0x3e, 0x66, 0x6c, 0x18, 0x18, 0x18, 0x66, 0x18, 0x00, 0x00, 0x00, 0x06, 0x66, 0x38, 0x66, 0x66,
- 0x3c, 0x60, 0x30, 0x06, 0x66, 0x66, 0x18, 0x18, 0x06, 0x00, 0x60, 0x66, 0xc6, 0x66, 0x66, 0x30,
- 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x06, 0xcc, 0x60, 0xee, 0xe6, 0x66, 0x66, 0xcc, 0x66, 0x66,
- 0x18, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x0c, 0x30, 0x60, 0x0c, 0x38, 0x00, 0x18, 0x00, 0x60, 0x00,
- 0x06, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x9c, 0x3c, 0x7e, 0x00, 0x0c, 0x36,
- 0x3c, 0x66, 0x18, 0x60, 0x66, 0x81, 0x24, 0x33, 0x06, 0x81, 0x00, 0x66, 0x18, 0x0c, 0x0c, 0x30,
- 0x00, 0x7a, 0x00, 0x00, 0x70, 0x44, 0xcc, 0xc6, 0xc6, 0x23, 0x00, 0x66, 0x18, 0x00, 0x1c, 0x00,
- 0x24, 0x60, 0x00, 0x1c, 0x18, 0x18, 0x00, 0x66, 0xcc, 0x00, 0x60, 0x3c, 0x30, 0xc6, 0x18, 0x00,
- 0x8e, 0x00, 0xc6, 0x66, 0x60, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x00,
- 0x24, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x7e,
- 0x8e, 0x66, 0x18, 0x00, 0x18, 0x18, 0x00, 0x66, 0x00, 0x18, 0x00, 0x18, 0x00, 0xfe, 0x60, 0xac,
- 0x68, 0x30, 0x30, 0x0c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x78, 0x06, 0x06, 0x6c, 0x7c,
- 0x60, 0x06, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x06, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60,
- 0x60, 0x60, 0x66, 0x18, 0x06, 0xd8, 0x60, 0xfe, 0xf6, 0x66, 0x66, 0xcc, 0x66, 0x70, 0x18, 0x66,
- 0x66, 0xc6, 0x3c, 0x3c, 0x18, 0x30, 0x30, 0x0c, 0x6c, 0x00, 0x0c, 0x3c, 0x7c, 0x3c, 0x3e, 0x3c,
- 0x7c, 0x3e, 0x7c, 0x18, 0x0c, 0x66, 0x18, 0xec, 0x7c, 0x3c, 0x7c, 0x3e, 0x7c, 0x3c, 0x7c, 0x66,
- 0x66, 0xc6, 0xc6, 0x66, 0x7e, 0x18, 0x18, 0x18, 0x00, 0xf0, 0x66, 0x18, 0x3e, 0x30, 0x66, 0x3c,
- 0x18, 0x3c, 0x00, 0x9d, 0x44, 0x66, 0x00, 0xb9, 0x00, 0x3c, 0x7e, 0x18, 0x18, 0x60, 0x66, 0x7a,
- 0x18, 0x00, 0x30, 0x44, 0x66, 0x4c, 0x4c, 0x66, 0x18, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x60,
- 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x60, 0xd8, 0x3c, 0x60, 0x66, 0xc6, 0xe6, 0x3c, 0x3c, 0x3c, 0x3c,
- 0x6c, 0x66, 0x6c, 0x66, 0x66, 0x66, 0x7e, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0x3c, 0x3c, 0x3c,
- 0x3c, 0x18, 0x3c, 0x7e, 0x3c, 0x3e, 0x6c, 0x00, 0x18, 0x3c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x66, 0x1e, 0x3c, 0x66, 0x00, 0x7e, 0x7e, 0x00, 0x18, 0x00, 0x6c, 0x3c, 0xd8, 0x76, 0x00,
- 0x30, 0x0c, 0xff, 0x7e, 0x00, 0x7e, 0x00, 0x18, 0x7e, 0x18, 0x0c, 0x1c, 0xcc, 0x06, 0x7c, 0x0c,
- 0x3c, 0x3e, 0x00, 0x00, 0x60, 0x00, 0x06, 0x0c, 0xd6, 0x7e, 0x7c, 0x60, 0x66, 0x78, 0x78, 0x6e,
- 0x7e, 0x18, 0x06, 0xf0, 0x60, 0xd6, 0xde, 0x66, 0x7c, 0xcc, 0x7c, 0x3c, 0x18, 0x66, 0x66, 0xd6,
- 0x18, 0x18, 0x30, 0x30, 0x18, 0x0c, 0xc6, 0x00, 0x00, 0x06, 0x66, 0x60, 0x66, 0x66, 0x30, 0x66,
- 0x66, 0x18, 0x0c, 0x6c, 0x18, 0xfe, 0x66, 0x66, 0x66, 0x66, 0x66, 0x60, 0x30, 0x66, 0x66, 0xc6,
- 0x6c, 0x66, 0x0c, 0x70, 0x18, 0x0e, 0x00, 0xc3, 0x66, 0x18, 0x6c, 0x78, 0x3c, 0x18, 0x00, 0x66,
- 0x00, 0xb1, 0x3c, 0xcc, 0x00, 0xa5, 0x00, 0x00, 0x18, 0x30, 0x0c, 0x00, 0x66, 0x3a, 0x18, 0x00,
- 0x30, 0x38, 0x33, 0x58, 0x58, 0x2c, 0x30, 0x7e, 0x18, 0x66, 0x66, 0x66, 0x66, 0x78, 0x60, 0x66,
- 0x60, 0x4c, 0x60, 0x6e, 0xf0, 0x18, 0x60, 0x30, 0xe6, 0xf6, 0x66, 0x66, 0x66, 0x66, 0x38, 0x66,
- 0x70, 0x30, 0x66, 0x66, 0x4c, 0x4c, 0x6c, 0x06, 0x18, 0x06, 0x3c, 0x06, 0x06, 0x66, 0x66, 0x3c,
- 0x66, 0x0c, 0x66, 0x66, 0x78, 0x18, 0x18, 0x60, 0x7c, 0x66, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x66,
- 0x78, 0x60, 0x66, 0x66, 0x0c, 0x0c, 0x00, 0x18, 0x00, 0xfe, 0x06, 0x36, 0xdc, 0x00, 0x30, 0x0c,
- 0x3c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x76, 0x18, 0x18, 0x06, 0xfe, 0x06, 0x66, 0x18, 0x66, 0x06,
- 0x00, 0x00, 0x18, 0x7e, 0x18, 0x18, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60, 0x60, 0x66, 0x66, 0x18,
- 0x06, 0xd8, 0x60, 0xc6, 0xce, 0x66, 0x60, 0xcc, 0x6c, 0x0e, 0x18, 0x66, 0x3c, 0xfe, 0x3c, 0x18,
- 0x60, 0x30, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x60, 0x66, 0x7e, 0x30, 0x66, 0x66, 0x18,
- 0x0c, 0x78, 0x18, 0xd6, 0x66, 0x66, 0x66, 0x66, 0x60, 0x3c, 0x30, 0x66, 0x66, 0xd6, 0x38, 0x66,
- 0x18, 0x18, 0x18, 0x18, 0x00, 0x0f, 0x66, 0x18, 0x3e, 0x30, 0x42, 0x3c, 0x18, 0x3c, 0x00, 0x9d,
- 0x00, 0x66, 0x00, 0xb9, 0x00, 0x00, 0x18, 0x7c, 0x78, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x30, 0x00,
- 0x66, 0x32, 0x3e, 0xd9, 0x60, 0x66, 0x18, 0x7e, 0x40, 0x7e, 0x7e, 0x60, 0x78, 0x40, 0x78, 0x18,
- 0x78, 0x66, 0xd8, 0x18, 0x60, 0x0c, 0xf6, 0xde, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x66, 0xe0, 0x0c,
- 0x66, 0x66, 0x18, 0x18, 0x66, 0x3e, 0x18, 0x3e, 0x60, 0x3e, 0x3e, 0x7e, 0x7e, 0x60, 0x7e, 0x18,
- 0x7e, 0x66, 0x6c, 0x18, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x18, 0x3c,
- 0x66, 0x66, 0x18, 0x18, 0x00, 0x00, 0x00, 0x6c, 0x7c, 0x6a, 0xce, 0x00, 0x18, 0x18, 0x66, 0x18,
- 0x18, 0x00, 0x18, 0x60, 0x66, 0x18, 0x30, 0x66, 0x0c, 0x66, 0x66, 0x18, 0x66, 0x0c, 0x18, 0x18,
- 0x06, 0x00, 0x60, 0x00, 0xc0, 0x66, 0x66, 0x30, 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x66, 0xcc,
- 0x60, 0xc6, 0xc6, 0x66, 0x60, 0xdc, 0x66, 0x66, 0x18, 0x66, 0x3c, 0xee, 0x66, 0x18, 0xc0, 0x30,
- 0x06, 0x0c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x60, 0x66, 0x60, 0x30, 0x3e, 0x66, 0x18, 0x0c, 0x6c,
- 0x18, 0xc6, 0x66, 0x66, 0x7c, 0x3e, 0x60, 0x06, 0x30, 0x66, 0x3c, 0xfe, 0x6c, 0x3c, 0x30, 0x18,
- 0x18, 0x18, 0x00, 0x3c, 0x66, 0x18, 0x0c, 0x30, 0x00, 0x18, 0x18, 0x06, 0x00, 0x81, 0x7e, 0x33,
- 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0x66,
- 0x62, 0x33, 0x66, 0x66, 0x18, 0x66, 0x66, 0x66, 0x66, 0x60, 0x60, 0x66, 0x60, 0x32, 0x60, 0x3e,
- 0xcc, 0x18, 0x7e, 0x66, 0xde, 0xce, 0x66, 0x66, 0x66, 0x66, 0xc6, 0x66, 0x60, 0x66, 0x66, 0x66,
- 0x32, 0x32, 0x66, 0x66, 0x18, 0x66, 0x60, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x30, 0x60, 0x3e,
- 0x66, 0x18, 0x18, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x18, 0x66, 0x18, 0x06, 0x66, 0x66,
- 0x30, 0x30, 0x00, 0x18, 0x00, 0x6c, 0x18, 0xcc, 0x7b, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x00,
- 0x18, 0xc0, 0x3c, 0x18, 0x7e, 0x3c, 0x0c, 0x3c, 0x3c, 0x18, 0x3c, 0x38, 0x18, 0x18, 0x00, 0x00,
- 0x00, 0x18, 0x78, 0x66, 0x7c, 0x1e, 0x78, 0x7e, 0x60, 0x3e, 0x66, 0x3c, 0x3c, 0xc6, 0x7e, 0xc6,
- 0xc6, 0x3c, 0x60, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0xc6, 0xc3, 0x18, 0xfe, 0x3c, 0x03, 0x3c,
- 0x00, 0x00, 0x00, 0x3e, 0x7c, 0x3c, 0x3e, 0x3c, 0x30, 0x06, 0x66, 0x0c, 0x0c, 0x66, 0x0c, 0xc6,
- 0x66, 0x3c, 0x60, 0x06, 0x60, 0x7c, 0x1c, 0x3e, 0x18, 0x6c, 0xc6, 0x18, 0x7e, 0x0e, 0x18, 0x70,
- 0x00, 0xf0, 0x7e, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x81,
- 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7f, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0xcf, 0xc4, 0x67,
- 0x3c, 0x67, 0x3e, 0x66, 0x3c, 0x66, 0x66, 0x7f, 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x18, 0x30, 0x3c,
- 0x18, 0x3c, 0xce, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x7e, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e,
- 0x6c, 0x3f, 0x1e, 0x3e, 0x3c, 0x3e, 0x3e, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x3c, 0x06, 0x18, 0x0c,
- 0x0c, 0x7c, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x0c, 0x7c, 0x3e, 0x3e, 0x7e, 0x7e,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x01, 0x00, 0x03,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x00, 0x30, 0x00,
- 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x18, 0x00,
- 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x08, 0x00, 0x20,
- 0x00, 0x08, 0x00, 0x28, 0x00, 0x08, 0x00, 0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x00, 0x40,
- 0x00, 0x08, 0x00, 0x48, 0x00, 0x08, 0x00, 0x50, 0x00, 0x08, 0x00, 0x58, 0x00, 0x08, 0x00, 0x60,
- 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x00, 0x70, 0x00, 0x08, 0x00, 0x78, 0x00, 0x08, 0x00, 0x80,
- 0x00, 0x08, 0x00, 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0x98, 0x00, 0x08, 0x00, 0xa0,
- 0x00, 0x08, 0x00, 0xa8, 0x00, 0x08, 0x00, 0xb0, 0x00, 0x08, 0x00, 0xb8, 0x00, 0x08, 0x00, 0xc0,
- 0x00, 0x08, 0x00, 0xc8, 0x00, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x00, 0xd8, 0x00, 0x08, 0x00, 0xe0,
- 0x00, 0x08, 0x00, 0xe8, 0x00, 0x08, 0x00, 0xf0, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x08, 0x01, 0x00,
- 0x00, 0x08, 0x01, 0x08, 0x00, 0x08, 0x01, 0x10, 0x00, 0x08, 0x01, 0x18, 0x00, 0x08, 0x01, 0x20,
- 0x00, 0x08, 0x01, 0x28, 0x00, 0x08, 0x01, 0x30, 0x00, 0x08, 0x01, 0x38, 0x00, 0x08, 0x01, 0x40,
- 0x00, 0x08, 0x01, 0x48, 0x00, 0x08, 0x01, 0x50, 0x00, 0x08, 0x01, 0x58, 0x00, 0x08, 0x01, 0x60,
- 0x00, 0x08, 0x01, 0x68, 0x00, 0x08, 0x01, 0x70, 0x00, 0x08, 0x01, 0x78, 0x00, 0x08, 0x01, 0x80,
- 0x00, 0x08, 0x01, 0x88, 0x00, 0x08, 0x01, 0x90, 0x00, 0x08, 0x01, 0x98, 0x00, 0x08, 0x01, 0xa0,
- 0x00, 0x08, 0x01, 0xa8, 0x00, 0x08, 0x01, 0xb0, 0x00, 0x08, 0x01, 0xb8, 0x00, 0x08, 0x01, 0xc0,
- 0x00, 0x08, 0x01, 0xc8, 0x00, 0x08, 0x01, 0xd0, 0x00, 0x08, 0x01, 0xd8, 0x00, 0x08, 0x01, 0xe0,
- 0x00, 0x08, 0x01, 0xe8, 0x00, 0x08, 0x01, 0xf0, 0x00, 0x08, 0x01, 0xf8, 0x00, 0x08, 0x02, 0x00,
- 0x00, 0x08, 0x02, 0x08, 0x00, 0x08, 0x02, 0x10, 0x00, 0x08, 0x02, 0x18, 0x00, 0x08, 0x02, 0x20,
- 0x00, 0x08, 0x02, 0x28, 0x00, 0x08, 0x02, 0x30, 0x00, 0x08, 0x02, 0x38, 0x00, 0x08, 0x02, 0x40,
- 0x00, 0x08, 0x02, 0x48, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x60,
- 0x00, 0x08, 0x02, 0x68, 0x00, 0x08, 0x02, 0x70, 0x00, 0x08, 0x02, 0x78, 0x00, 0x08, 0x02, 0x80,
- 0x00, 0x08, 0x02, 0x88, 0x00, 0x08, 0x02, 0x90, 0x00, 0x08, 0x02, 0x98, 0x00, 0x08, 0x02, 0xa0,
- 0x00, 0x08, 0x02, 0xa8, 0x00, 0x08, 0x02, 0xb0, 0x00, 0x08, 0x02, 0xb8, 0x00, 0x08, 0x02, 0xc0,
- 0x00, 0x08, 0x02, 0xc8, 0x00, 0x08, 0x02, 0xd0, 0x00, 0x08, 0x02, 0xd8, 0x00, 0x08, 0x02, 0xe0,
- 0x00, 0x08, 0x02, 0xe8, 0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00,
- 0x00, 0x08, 0x03, 0x08, 0x00, 0x08, 0x03, 0x10, 0x00, 0x08, 0x03, 0x18, 0x00, 0x08, 0x03, 0x20,
- 0x00, 0x08, 0x03, 0x28, 0x00, 0x08, 0x03, 0x30, 0x00, 0x08, 0x03, 0x38, 0x00, 0x08, 0x03, 0x40,
- 0x00, 0x08, 0x03, 0x48, 0x00, 0x08, 0x03, 0x50, 0x00, 0x08, 0x03, 0x58, 0x00, 0x08, 0x03, 0x60,
- 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x03, 0x68, 0x00, 0x08, 0x03, 0x70, 0x00, 0x08, 0x03, 0x78,
- 0x00, 0x08, 0x03, 0x80, 0x00, 0x08, 0x03, 0x88, 0x00, 0x08, 0x03, 0x90, 0x00, 0x08, 0x03, 0x98,
- 0x00, 0x08, 0x03, 0xa0, 0x00, 0x08, 0x03, 0xa8, 0x00, 0x08, 0x03, 0xb0, 0x00, 0x08, 0x03, 0xb8,
- 0x00, 0x08, 0x03, 0xc0, 0x00, 0x08, 0x03, 0xc8, 0x00, 0x08, 0x03, 0xd0, 0x00, 0x08, 0x03, 0xd8,
- 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08, 0x03, 0xe8, 0x00, 0x08, 0x03, 0xf0, 0x00, 0x08, 0x03, 0xf8,
- 0x00, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x08, 0x00, 0x08, 0x04, 0x10, 0x00, 0x08, 0x04, 0x18,
- 0x00, 0x08, 0x04, 0x20, 0x00, 0x08, 0x04, 0x28, 0x00, 0x08, 0x04, 0x30, 0x00, 0x08, 0x04, 0x38,
- 0x00, 0x08, 0x04, 0x40, 0x00, 0x08, 0x04, 0x48, 0x00, 0x08, 0x04, 0x50, 0x00, 0x08, 0x04, 0x58,
- 0x00, 0x08, 0x04, 0x60, 0x00, 0x08, 0x04, 0x68, 0x00, 0x08, 0x04, 0x70, 0x00, 0x08, 0x04, 0x78,
- 0x00, 0x08, 0x04, 0x80, 0x00, 0x08, 0x04, 0x88, 0x00, 0x08, 0x04, 0x90, 0x00, 0x08, 0x04, 0x98,
- 0x00, 0x08, 0x04, 0xa0, 0x00, 0x08, 0x04, 0xa8, 0x00, 0x08, 0x04, 0xb0, 0x00, 0x08, 0x04, 0xb8,
- 0x00, 0x08, 0x04, 0xc0, 0x00, 0x08, 0x04, 0xc8, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd8,
- 0x00, 0x08, 0x04, 0xe0, 0x00, 0x08, 0x04, 0xe8, 0x00, 0x08, 0x04, 0xf0, 0x00, 0x08, 0x04, 0xf8,
- 0x00, 0x08, 0x05, 0x00, 0x00, 0x08, 0x05, 0x08, 0x00, 0x08, 0x05, 0x10, 0x00, 0x08, 0x05, 0x18,
- 0x00, 0x08, 0x05, 0x20, 0x00, 0x08, 0x05, 0x28, 0x00, 0x08, 0x05, 0x30, 0x00, 0x08, 0x05, 0x38,
- 0x00, 0x08, 0x05, 0x40, 0x00, 0x08, 0x05, 0x48, 0x00, 0x08, 0x05, 0x50, 0x00, 0x08, 0x05, 0x58,
- 0x00, 0x08, 0x05, 0x60, 0x00, 0x08, 0x05, 0x68, 0x00, 0x08, 0x05, 0x70, 0x00, 0x08, 0x05, 0x78,
- 0x00, 0x08, 0x05, 0x80, 0x00, 0x08, 0x05, 0x88, 0x00, 0x08, 0x05, 0x90, 0x00, 0x08, 0x05, 0x98,
- 0x00, 0x08, 0x05, 0xa0, 0x00, 0x08, 0x05, 0xa8, 0x00, 0x08, 0x05, 0xb0, 0x00, 0x08, 0x05, 0xb8,
- 0x00, 0x08, 0x05, 0xc0, 0x00, 0x08, 0x05, 0xc8, 0x00, 0x08, 0x05, 0xd0, 0x00, 0x08, 0x05, 0xd8,
- 0x00, 0x08, 0x05, 0xe0, 0x00, 0x08, 0x05, 0xe8, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x03, 0x00,
- 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf2
+ 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79,
+ 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x1a, 0x0f, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x45, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x74, 0x00, 0x08,
+ 0x00, 0x40, 0x00, 0x08, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x20, 0xff, 0x00, 0x00, 0x00, 0x6e,
+ 0x00, 0xbe, 0x00, 0x00, 0x06, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x6c, 0x6c, 0x18, 0x00, 0x38, 0x18, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, 0x18,
+ 0x3c, 0x3c, 0x1c, 0x7e, 0x1c, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7c, 0x3c,
+ 0x7c, 0x1e, 0x78, 0x7e, 0x7e, 0x3c, 0x66, 0x3c, 0x06, 0xc6, 0x60, 0xc6, 0xc6, 0x3c, 0x7c, 0x78,
+ 0x7c, 0x3c, 0x7e, 0x66, 0x66, 0xc6, 0xc3, 0xc3, 0xfe, 0x3c, 0xc0, 0x3c, 0x10, 0x00, 0x18, 0x00,
+ 0x60, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x60, 0x18, 0x0c, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x70, 0x72, 0x0f, 0x00, 0x18,
+ 0x00, 0x1c, 0x42, 0xc3, 0x18, 0x3c, 0x66, 0x7e, 0x1c, 0x00, 0x3e, 0x7e, 0x7e, 0x3c, 0x18, 0x78,
+ 0x78, 0x18, 0x00, 0x3e, 0x00, 0x00, 0x30, 0x38, 0x00, 0x40, 0x40, 0xc0, 0x18, 0x3c, 0x3c, 0x7e,
+ 0x06, 0x66, 0x18, 0x7e, 0x7e, 0x36, 0x0c, 0x0c, 0x18, 0x3c, 0xc6, 0x3c, 0x60, 0x76, 0x18, 0x00,
+ 0x0c, 0x7e, 0x71, 0x66, 0x00, 0x66, 0x60, 0x0e, 0x7e, 0x66, 0x18, 0x6e, 0x3c, 0x00, 0x18, 0x7e,
+ 0x06, 0x66, 0x18, 0x00, 0x7e, 0x34, 0x0c, 0x0c, 0x18, 0x0c, 0x60, 0x00, 0x18, 0x3c, 0x0c, 0x00,
+ 0x0c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x7e, 0x00, 0x18, 0x3c, 0x00, 0x18, 0x6c, 0x6c,
+ 0x3e, 0x66, 0x6c, 0x18, 0x18, 0x18, 0x66, 0x18, 0x00, 0x00, 0x00, 0x06, 0x66, 0x38, 0x66, 0x66,
+ 0x3c, 0x60, 0x30, 0x06, 0x66, 0x66, 0x18, 0x18, 0x06, 0x00, 0x60, 0x66, 0xc6, 0x66, 0x66, 0x30,
+ 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x06, 0xcc, 0x60, 0xee, 0xe6, 0x66, 0x66, 0xcc, 0x66, 0x66,
+ 0x18, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x0c, 0x30, 0x60, 0x0c, 0x38, 0x00, 0x18, 0x00, 0x60, 0x00,
+ 0x06, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x9c, 0x3c, 0x7e, 0x00, 0x0c, 0x36,
+ 0x3c, 0x66, 0x18, 0x60, 0x66, 0x81, 0x24, 0x33, 0x06, 0x81, 0x00, 0x66, 0x18, 0x0c, 0x0c, 0x30,
+ 0x00, 0x7a, 0x00, 0x00, 0x70, 0x44, 0xcc, 0xc6, 0xc6, 0x23, 0x00, 0x66, 0x18, 0x00, 0x1c, 0x00,
+ 0x24, 0x60, 0x00, 0x1c, 0x18, 0x18, 0x00, 0x66, 0xcc, 0x00, 0x60, 0x3c, 0x30, 0xc6, 0x18, 0x00,
+ 0x8e, 0x00, 0xc6, 0x66, 0x60, 0x38, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x00,
+ 0x24, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x7e,
+ 0x8e, 0x66, 0x18, 0x00, 0x18, 0x18, 0x00, 0x66, 0x00, 0x18, 0x00, 0x18, 0x00, 0xfe, 0x60, 0xac,
+ 0x68, 0x30, 0x30, 0x0c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x78, 0x06, 0x06, 0x6c, 0x7c,
+ 0x60, 0x06, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x06, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60,
+ 0x60, 0x60, 0x66, 0x18, 0x06, 0xd8, 0x60, 0xfe, 0xf6, 0x66, 0x66, 0xcc, 0x66, 0x70, 0x18, 0x66,
+ 0x66, 0xc6, 0x3c, 0x3c, 0x18, 0x30, 0x30, 0x0c, 0x6c, 0x00, 0x0c, 0x3c, 0x7c, 0x3c, 0x3e, 0x3c,
+ 0x7c, 0x3e, 0x7c, 0x18, 0x0c, 0x66, 0x18, 0xec, 0x7c, 0x3c, 0x7c, 0x3e, 0x7c, 0x3c, 0x7c, 0x66,
+ 0x66, 0xc6, 0xc6, 0x66, 0x7e, 0x18, 0x18, 0x18, 0x00, 0xf0, 0x66, 0x18, 0x3e, 0x30, 0x66, 0x3c,
+ 0x18, 0x3c, 0x00, 0x9d, 0x44, 0x66, 0x00, 0xb9, 0x00, 0x3c, 0x7e, 0x18, 0x18, 0x60, 0x66, 0x7a,
+ 0x18, 0x00, 0x30, 0x44, 0x66, 0x4c, 0x4c, 0x66, 0x18, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x60,
+ 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x60, 0xd8, 0x3c, 0x60, 0x66, 0xc6, 0xe6, 0x3c, 0x3c, 0x3c, 0x3c,
+ 0x6c, 0x66, 0x6c, 0x66, 0x66, 0x66, 0x7e, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0x3c, 0x3c, 0x3c,
+ 0x3c, 0x18, 0x3c, 0x7e, 0x3c, 0x3e, 0x6c, 0x00, 0x18, 0x3c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x1e, 0x3c, 0x66, 0x00, 0x7e, 0x7e, 0x00, 0x18, 0x00, 0x6c, 0x3c, 0xd8, 0x76, 0x00,
+ 0x30, 0x0c, 0xff, 0x7e, 0x00, 0x7e, 0x00, 0x18, 0x7e, 0x18, 0x0c, 0x1c, 0xcc, 0x06, 0x7c, 0x0c,
+ 0x3c, 0x3e, 0x00, 0x00, 0x60, 0x00, 0x06, 0x0c, 0xd6, 0x7e, 0x7c, 0x60, 0x66, 0x78, 0x78, 0x6e,
+ 0x7e, 0x18, 0x06, 0xf0, 0x60, 0xd6, 0xde, 0x66, 0x7c, 0xcc, 0x7c, 0x3c, 0x18, 0x66, 0x66, 0xd6,
+ 0x18, 0x18, 0x30, 0x30, 0x18, 0x0c, 0xc6, 0x00, 0x00, 0x06, 0x66, 0x60, 0x66, 0x66, 0x30, 0x66,
+ 0x66, 0x18, 0x0c, 0x6c, 0x18, 0xfe, 0x66, 0x66, 0x66, 0x66, 0x66, 0x60, 0x30, 0x66, 0x66, 0xc6,
+ 0x6c, 0x66, 0x0c, 0x70, 0x18, 0x0e, 0x00, 0xc3, 0x66, 0x18, 0x6c, 0x78, 0x3c, 0x18, 0x00, 0x66,
+ 0x00, 0xb1, 0x3c, 0xcc, 0x00, 0xa5, 0x00, 0x00, 0x18, 0x30, 0x0c, 0x00, 0x66, 0x3a, 0x18, 0x00,
+ 0x30, 0x38, 0x33, 0x58, 0x58, 0x2c, 0x30, 0x7e, 0x18, 0x66, 0x66, 0x66, 0x66, 0x78, 0x60, 0x66,
+ 0x60, 0x4c, 0x60, 0x6e, 0xf0, 0x18, 0x60, 0x30, 0xe6, 0xf6, 0x66, 0x66, 0x66, 0x66, 0x38, 0x66,
+ 0x70, 0x30, 0x66, 0x66, 0x4c, 0x4c, 0x6c, 0x06, 0x18, 0x06, 0x3c, 0x06, 0x06, 0x66, 0x66, 0x3c,
+ 0x66, 0x0c, 0x66, 0x66, 0x78, 0x18, 0x18, 0x60, 0x7c, 0x66, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x66,
+ 0x78, 0x60, 0x66, 0x66, 0x0c, 0x0c, 0x00, 0x18, 0x00, 0xfe, 0x06, 0x36, 0xdc, 0x00, 0x30, 0x0c,
+ 0x3c, 0x18, 0x00, 0x00, 0x00, 0x30, 0x76, 0x18, 0x18, 0x06, 0xfe, 0x06, 0x66, 0x18, 0x66, 0x06,
+ 0x00, 0x00, 0x18, 0x7e, 0x18, 0x18, 0xde, 0x66, 0x66, 0x60, 0x66, 0x60, 0x60, 0x66, 0x66, 0x18,
+ 0x06, 0xd8, 0x60, 0xc6, 0xce, 0x66, 0x60, 0xcc, 0x6c, 0x0e, 0x18, 0x66, 0x3c, 0xfe, 0x3c, 0x18,
+ 0x60, 0x30, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x60, 0x66, 0x7e, 0x30, 0x66, 0x66, 0x18,
+ 0x0c, 0x78, 0x18, 0xd6, 0x66, 0x66, 0x66, 0x66, 0x60, 0x3c, 0x30, 0x66, 0x66, 0xd6, 0x38, 0x66,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x0f, 0x66, 0x18, 0x3e, 0x30, 0x42, 0x3c, 0x18, 0x3c, 0x00, 0x9d,
+ 0x00, 0x66, 0x00, 0xb9, 0x00, 0x00, 0x18, 0x7c, 0x78, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x30, 0x00,
+ 0x66, 0x32, 0x3e, 0xd9, 0x60, 0x66, 0x18, 0x7e, 0x40, 0x7e, 0x7e, 0x60, 0x78, 0x40, 0x78, 0x18,
+ 0x78, 0x66, 0xd8, 0x18, 0x60, 0x0c, 0xf6, 0xde, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x66, 0xe0, 0x0c,
+ 0x66, 0x66, 0x18, 0x18, 0x66, 0x3e, 0x18, 0x3e, 0x60, 0x3e, 0x3e, 0x7e, 0x7e, 0x60, 0x7e, 0x18,
+ 0x7e, 0x66, 0x6c, 0x18, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x18, 0x3c,
+ 0x66, 0x66, 0x18, 0x18, 0x00, 0x00, 0x00, 0x6c, 0x7c, 0x6a, 0xce, 0x00, 0x18, 0x18, 0x66, 0x18,
+ 0x18, 0x00, 0x18, 0x60, 0x66, 0x18, 0x30, 0x66, 0x0c, 0x66, 0x66, 0x18, 0x66, 0x0c, 0x18, 0x18,
+ 0x06, 0x00, 0x60, 0x00, 0xc0, 0x66, 0x66, 0x30, 0x6c, 0x60, 0x60, 0x66, 0x66, 0x18, 0x66, 0xcc,
+ 0x60, 0xc6, 0xc6, 0x66, 0x60, 0xdc, 0x66, 0x66, 0x18, 0x66, 0x3c, 0xee, 0x66, 0x18, 0xc0, 0x30,
+ 0x06, 0x0c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x60, 0x66, 0x60, 0x30, 0x3e, 0x66, 0x18, 0x0c, 0x6c,
+ 0x18, 0xc6, 0x66, 0x66, 0x7c, 0x3e, 0x60, 0x06, 0x30, 0x66, 0x3c, 0xfe, 0x6c, 0x3c, 0x30, 0x18,
+ 0x18, 0x18, 0x00, 0x3c, 0x66, 0x18, 0x0c, 0x30, 0x00, 0x18, 0x18, 0x06, 0x00, 0x81, 0x7e, 0x33,
+ 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x0a, 0x00, 0x00, 0x00, 0x7c, 0xcc, 0x66,
+ 0x62, 0x33, 0x66, 0x66, 0x18, 0x66, 0x66, 0x66, 0x66, 0x60, 0x60, 0x66, 0x60, 0x32, 0x60, 0x3e,
+ 0xcc, 0x18, 0x7e, 0x66, 0xde, 0xce, 0x66, 0x66, 0x66, 0x66, 0xc6, 0x66, 0x60, 0x66, 0x66, 0x66,
+ 0x32, 0x32, 0x66, 0x66, 0x18, 0x66, 0x60, 0x66, 0x66, 0x60, 0x60, 0x60, 0x60, 0x30, 0x60, 0x3e,
+ 0x66, 0x18, 0x18, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x18, 0x66, 0x18, 0x06, 0x66, 0x66,
+ 0x30, 0x30, 0x00, 0x18, 0x00, 0x6c, 0x18, 0xcc, 0x7b, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x00,
+ 0x18, 0xc0, 0x3c, 0x18, 0x7e, 0x3c, 0x0c, 0x3c, 0x3c, 0x18, 0x3c, 0x38, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x18, 0x78, 0x66, 0x7c, 0x1e, 0x78, 0x7e, 0x60, 0x3e, 0x66, 0x3c, 0x3c, 0xc6, 0x7e, 0xc6,
+ 0xc6, 0x3c, 0x60, 0x7e, 0x66, 0x3c, 0x18, 0x3c, 0x18, 0xc6, 0xc3, 0x18, 0xfe, 0x3c, 0x03, 0x3c,
+ 0x00, 0x00, 0x00, 0x3e, 0x7c, 0x3c, 0x3e, 0x3c, 0x30, 0x06, 0x66, 0x0c, 0x0c, 0x66, 0x0c, 0xc6,
+ 0x66, 0x3c, 0x60, 0x06, 0x60, 0x7c, 0x1c, 0x3e, 0x18, 0x6c, 0xc6, 0x18, 0x7e, 0x0e, 0x18, 0x70,
+ 0x00, 0xf0, 0x7e, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x3c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x81,
+ 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7f, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0xcf, 0xc4, 0x67,
+ 0x3c, 0x67, 0x3e, 0x66, 0x3c, 0x66, 0x66, 0x7f, 0x7e, 0x3c, 0x7e, 0x7e, 0x7e, 0x18, 0x30, 0x3c,
+ 0x18, 0x3c, 0xce, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x7e, 0x3c, 0x3c, 0x3c, 0x7e, 0x7e,
+ 0x6c, 0x3f, 0x1e, 0x3e, 0x3c, 0x3e, 0x3e, 0x3c, 0x3c, 0x3c, 0x3c, 0x7e, 0x3c, 0x06, 0x18, 0x0c,
+ 0x0c, 0x7c, 0x66, 0x18, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x3f, 0x0c, 0x7c, 0x3e, 0x3e, 0x7e, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x01, 0x00, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x00, 0x30, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x18, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x08, 0x00, 0x20,
+ 0x00, 0x08, 0x00, 0x28, 0x00, 0x08, 0x00, 0x30, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x00, 0x40,
+ 0x00, 0x08, 0x00, 0x48, 0x00, 0x08, 0x00, 0x50, 0x00, 0x08, 0x00, 0x58, 0x00, 0x08, 0x00, 0x60,
+ 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x00, 0x70, 0x00, 0x08, 0x00, 0x78, 0x00, 0x08, 0x00, 0x80,
+ 0x00, 0x08, 0x00, 0x88, 0x00, 0x08, 0x00, 0x90, 0x00, 0x08, 0x00, 0x98, 0x00, 0x08, 0x00, 0xa0,
+ 0x00, 0x08, 0x00, 0xa8, 0x00, 0x08, 0x00, 0xb0, 0x00, 0x08, 0x00, 0xb8, 0x00, 0x08, 0x00, 0xc0,
+ 0x00, 0x08, 0x00, 0xc8, 0x00, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x00, 0xd8, 0x00, 0x08, 0x00, 0xe0,
+ 0x00, 0x08, 0x00, 0xe8, 0x00, 0x08, 0x00, 0xf0, 0x00, 0x08, 0x00, 0xf8, 0x00, 0x08, 0x01, 0x00,
+ 0x00, 0x08, 0x01, 0x08, 0x00, 0x08, 0x01, 0x10, 0x00, 0x08, 0x01, 0x18, 0x00, 0x08, 0x01, 0x20,
+ 0x00, 0x08, 0x01, 0x28, 0x00, 0x08, 0x01, 0x30, 0x00, 0x08, 0x01, 0x38, 0x00, 0x08, 0x01, 0x40,
+ 0x00, 0x08, 0x01, 0x48, 0x00, 0x08, 0x01, 0x50, 0x00, 0x08, 0x01, 0x58, 0x00, 0x08, 0x01, 0x60,
+ 0x00, 0x08, 0x01, 0x68, 0x00, 0x08, 0x01, 0x70, 0x00, 0x08, 0x01, 0x78, 0x00, 0x08, 0x01, 0x80,
+ 0x00, 0x08, 0x01, 0x88, 0x00, 0x08, 0x01, 0x90, 0x00, 0x08, 0x01, 0x98, 0x00, 0x08, 0x01, 0xa0,
+ 0x00, 0x08, 0x01, 0xa8, 0x00, 0x08, 0x01, 0xb0, 0x00, 0x08, 0x01, 0xb8, 0x00, 0x08, 0x01, 0xc0,
+ 0x00, 0x08, 0x01, 0xc8, 0x00, 0x08, 0x01, 0xd0, 0x00, 0x08, 0x01, 0xd8, 0x00, 0x08, 0x01, 0xe0,
+ 0x00, 0x08, 0x01, 0xe8, 0x00, 0x08, 0x01, 0xf0, 0x00, 0x08, 0x01, 0xf8, 0x00, 0x08, 0x02, 0x00,
+ 0x00, 0x08, 0x02, 0x08, 0x00, 0x08, 0x02, 0x10, 0x00, 0x08, 0x02, 0x18, 0x00, 0x08, 0x02, 0x20,
+ 0x00, 0x08, 0x02, 0x28, 0x00, 0x08, 0x02, 0x30, 0x00, 0x08, 0x02, 0x38, 0x00, 0x08, 0x02, 0x40,
+ 0x00, 0x08, 0x02, 0x48, 0x00, 0x08, 0x02, 0x50, 0x00, 0x08, 0x02, 0x58, 0x00, 0x08, 0x02, 0x60,
+ 0x00, 0x08, 0x02, 0x68, 0x00, 0x08, 0x02, 0x70, 0x00, 0x08, 0x02, 0x78, 0x00, 0x08, 0x02, 0x80,
+ 0x00, 0x08, 0x02, 0x88, 0x00, 0x08, 0x02, 0x90, 0x00, 0x08, 0x02, 0x98, 0x00, 0x08, 0x02, 0xa0,
+ 0x00, 0x08, 0x02, 0xa8, 0x00, 0x08, 0x02, 0xb0, 0x00, 0x08, 0x02, 0xb8, 0x00, 0x08, 0x02, 0xc0,
+ 0x00, 0x08, 0x02, 0xc8, 0x00, 0x08, 0x02, 0xd0, 0x00, 0x08, 0x02, 0xd8, 0x00, 0x08, 0x02, 0xe0,
+ 0x00, 0x08, 0x02, 0xe8, 0x00, 0x08, 0x02, 0xf0, 0x00, 0x08, 0x02, 0xf8, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x08, 0x03, 0x08, 0x00, 0x08, 0x03, 0x10, 0x00, 0x08, 0x03, 0x18, 0x00, 0x08, 0x03, 0x20,
+ 0x00, 0x08, 0x03, 0x28, 0x00, 0x08, 0x03, 0x30, 0x00, 0x08, 0x03, 0x38, 0x00, 0x08, 0x03, 0x40,
+ 0x00, 0x08, 0x03, 0x48, 0x00, 0x08, 0x03, 0x50, 0x00, 0x08, 0x03, 0x58, 0x00, 0x08, 0x03, 0x60,
+ 0x00, 0x08, 0x00, 0x68, 0x00, 0x08, 0x03, 0x68, 0x00, 0x08, 0x03, 0x70, 0x00, 0x08, 0x03, 0x78,
+ 0x00, 0x08, 0x03, 0x80, 0x00, 0x08, 0x03, 0x88, 0x00, 0x08, 0x03, 0x90, 0x00, 0x08, 0x03, 0x98,
+ 0x00, 0x08, 0x03, 0xa0, 0x00, 0x08, 0x03, 0xa8, 0x00, 0x08, 0x03, 0xb0, 0x00, 0x08, 0x03, 0xb8,
+ 0x00, 0x08, 0x03, 0xc0, 0x00, 0x08, 0x03, 0xc8, 0x00, 0x08, 0x03, 0xd0, 0x00, 0x08, 0x03, 0xd8,
+ 0x00, 0x08, 0x03, 0xe0, 0x00, 0x08, 0x03, 0xe8, 0x00, 0x08, 0x03, 0xf0, 0x00, 0x08, 0x03, 0xf8,
+ 0x00, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x08, 0x00, 0x08, 0x04, 0x10, 0x00, 0x08, 0x04, 0x18,
+ 0x00, 0x08, 0x04, 0x20, 0x00, 0x08, 0x04, 0x28, 0x00, 0x08, 0x04, 0x30, 0x00, 0x08, 0x04, 0x38,
+ 0x00, 0x08, 0x04, 0x40, 0x00, 0x08, 0x04, 0x48, 0x00, 0x08, 0x04, 0x50, 0x00, 0x08, 0x04, 0x58,
+ 0x00, 0x08, 0x04, 0x60, 0x00, 0x08, 0x04, 0x68, 0x00, 0x08, 0x04, 0x70, 0x00, 0x08, 0x04, 0x78,
+ 0x00, 0x08, 0x04, 0x80, 0x00, 0x08, 0x04, 0x88, 0x00, 0x08, 0x04, 0x90, 0x00, 0x08, 0x04, 0x98,
+ 0x00, 0x08, 0x04, 0xa0, 0x00, 0x08, 0x04, 0xa8, 0x00, 0x08, 0x04, 0xb0, 0x00, 0x08, 0x04, 0xb8,
+ 0x00, 0x08, 0x04, 0xc0, 0x00, 0x08, 0x04, 0xc8, 0x00, 0x08, 0x04, 0xd0, 0x00, 0x08, 0x04, 0xd8,
+ 0x00, 0x08, 0x04, 0xe0, 0x00, 0x08, 0x04, 0xe8, 0x00, 0x08, 0x04, 0xf0, 0x00, 0x08, 0x04, 0xf8,
+ 0x00, 0x08, 0x05, 0x00, 0x00, 0x08, 0x05, 0x08, 0x00, 0x08, 0x05, 0x10, 0x00, 0x08, 0x05, 0x18,
+ 0x00, 0x08, 0x05, 0x20, 0x00, 0x08, 0x05, 0x28, 0x00, 0x08, 0x05, 0x30, 0x00, 0x08, 0x05, 0x38,
+ 0x00, 0x08, 0x05, 0x40, 0x00, 0x08, 0x05, 0x48, 0x00, 0x08, 0x05, 0x50, 0x00, 0x08, 0x05, 0x58,
+ 0x00, 0x08, 0x05, 0x60, 0x00, 0x08, 0x05, 0x68, 0x00, 0x08, 0x05, 0x70, 0x00, 0x08, 0x05, 0x78,
+ 0x00, 0x08, 0x05, 0x80, 0x00, 0x08, 0x05, 0x88, 0x00, 0x08, 0x05, 0x90, 0x00, 0x08, 0x05, 0x98,
+ 0x00, 0x08, 0x05, 0xa0, 0x00, 0x08, 0x05, 0xa8, 0x00, 0x08, 0x05, 0xb0, 0x00, 0x08, 0x05, 0xb8,
+ 0x00, 0x08, 0x05, 0xc0, 0x00, 0x08, 0x05, 0xc8, 0x00, 0x08, 0x05, 0xd0, 0x00, 0x08, 0x05, 0xd8,
+ 0x00, 0x08, 0x05, 0xe0, 0x00, 0x08, 0x05, 0xe8, 0x00, 0x08, 0x00, 0x38, 0x00, 0x08, 0x03, 0x00,
+ 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, 0x03, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x62,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf2
};
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/queen.h b/engines/queen/queen.h
index 5a8f1357f4..93d705b182 100644
--- a/engines/queen/queen.h
+++ b/engines/queen/queen.h
@@ -56,8 +56,8 @@ FORCEINLINE int16 READ_BE_INT16(const void *ptr) {
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Flight of the Amazon Queen
*/
namespace Queen {
diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp
index 27b1e9c60a..106b0d6123 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;
@@ -777,7 +777,7 @@ void Talk::defaultAnimation(
}
// Make sure that Person closes their mouth
- if (!isJoe && parameters->ff > 0)
+ if (!isJoe && parameters && parameters->ff > 0)
_vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
}
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index 8bc8025032..5111f1ae07 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -42,18 +42,42 @@
namespace Saga {
ActorData::ActorData() {
- memset(this, 0, sizeof(*this));
-}
+ _frames = NULL;
+ _frameListResourceId = 0;
+ _speechColor = 0;
+ _inScene = false;
+
+ _actorFlags = 0;
+ _currentAction = 0;
+ _facingDirection = 0;
+ _actionDirection = 0;
+ _actionCycle = 0;
+ _targetObject = 0;
+ _lastZone = NULL;
+
+ _cycleFrameSequence = 0;
+ _cycleDelay = 0;
+ _cycleTimeCount = 0;
+ _cycleFlags = 0;
+
+ _fallVelocity = 0;
+ _fallAcceleration = 0;
+ _fallPosition = 0;
-ActorData::~ActorData() {
- if (!_shareFrames)
- free(_frames);
- free(_tileDirections);
- free(_walkStepsPoints);
- freeSpriteList();
+ _dragonBaseFrame = 0;
+ _dragonStepCycle = 0;
+ _dragonMoveType = 0;
+
+ _frameNumber = 0;
+
+ _walkStepsCount = 0;
+ _walkStepIndex = 0;
+
+ _walkFrameSequence = 0;
}
+
void ActorData::saveState(Common::OutSaveFile *out) {
- int i = 0;
+ uint i = 0;
CommonObjectData::saveState(out);
out->writeUint16LE(_actorFlags);
out->writeSint32LE(_currentAction);
@@ -74,13 +98,13 @@ void ActorData::saveState(Common::OutSaveFile *out) {
out->writeByte(_dragonMoveType);
out->writeSint32LE(_frameNumber);
- out->writeSint32LE(_tileDirectionsAlloced);
- for (i = 0; i < _tileDirectionsAlloced; i++) {
+ out->writeSint32LE(_tileDirections.size());
+ for (i = 0; i < _tileDirections.size(); i++) {
out->writeByte(_tileDirections[i]);
}
- out->writeSint32LE(_walkStepsAlloced);
- for (i = 0; i < _walkStepsAlloced; i++) {
+ out->writeSint32LE(_walkStepsPoints.size());
+ for (i = 0; i < _walkStepsPoints.size(); i++) {
out->writeSint16LE(_walkStepsPoints[i].x);
out->writeSint16LE(_walkStepsPoints[i].y);
}
@@ -93,7 +117,7 @@ void ActorData::saveState(Common::OutSaveFile *out) {
}
void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
- int i = 0;
+ uint i = 0;
CommonObjectData::loadState(in);
_actorFlags = in->readUint16LE();
_currentAction = in->readSint32LE();
@@ -125,13 +149,13 @@ void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
_frameNumber = in->readSint32LE();
- setTileDirectionsSize(in->readSint32LE(), true);
- for (i = 0; i < _tileDirectionsAlloced; i++) {
+ _tileDirections.resize(in->readSint32LE());
+ for (i = 0; i < _tileDirections.size(); i++) {
_tileDirections[i] = in->readByte();
}
- setWalkStepsPointsSize(in->readSint32LE(), true);
- for (i = 0; i < _walkStepsAlloced; i++) {
+ _walkStepsPoints.resize(in->readSint32LE());
+ for (i = 0; i < _walkStepsPoints.size(); i++) {
_walkStepsPoints[i].x = in->readSint16LE();
_walkStepsPoints[i].y = in->readSint16LE();
}
@@ -143,38 +167,16 @@ void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
_walkFrameSequence = in->readSint32LE();
}
-void ActorData::setTileDirectionsSize(int size, bool forceRealloc) {
- if ((size <= _tileDirectionsAlloced) && !forceRealloc) {
- return;
- }
- _tileDirectionsAlloced = size;
- _tileDirections = (byte*)realloc(_tileDirections, _tileDirectionsAlloced * sizeof(*_tileDirections));
-}
-
void ActorData::cycleWrap(int cycleLimit) {
if (_actionCycle >= cycleLimit)
_actionCycle = 0;
}
-void ActorData::setWalkStepsPointsSize(int size, bool forceRealloc) {
- if ((size <= _walkStepsAlloced) && !forceRealloc) {
- return;
- }
- _walkStepsAlloced = size;
- _walkStepsPoints = (Point*)realloc(_walkStepsPoints, _walkStepsAlloced * sizeof(*_walkStepsPoints));
-}
-
void ActorData::addWalkStepPoint(const Point &point) {
- setWalkStepsPointsSize(_walkStepsCount + 1, false);
+ _walkStepsPoints.resize(_walkStepsCount + 1);
_walkStepsPoints[_walkStepsCount++] = point;
}
-void ActorData::freeSpriteList() {
- _spriteList.freeMem();
-}
-
-
-
static int commonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
int p1 = obj1->_location.y - obj1->_location.z;
int p2 = obj2->_location.y - obj2->_location.z;
@@ -212,26 +214,14 @@ static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const Co
Actor::Actor(SagaEngine *vm) : _vm(vm) {
int i;
- byte *stringsPointer;
- size_t stringsLength;
- ActorData *actor;
- ObjectData *obj;
+ ByteArray stringsData;
debug(9, "Actor::Actor()");
_handleActionDiv = 15;
- _actors = NULL;
- _actorsCount = 0;
-
- _objs = NULL;
- _objsCount = 0;
-
#ifdef ACTOR_DEBUG
_debugPointsCount = 0;
#endif
- _protagStates = NULL;
- _protagStatesCount = 0;
-
_pathList.resize(600);
_pathListIndex = 0;
@@ -242,7 +232,7 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
_yCellCount = _vm->_scene->getHeight();
_xCellCount = _vm->getDisplayInfo().width;
- _pathCell = (int8 *)malloc(_yCellCount * _xCellCount * sizeof(*_pathCell));
+ _pathCell.resize(_yCellCount * _xCellCount);
_pathRect.left = 0;
_pathRect.right = _vm->getDisplayInfo().width;
@@ -260,19 +250,17 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
if (_vm->getGameId() == GID_ITE) {
- _vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsPointer, stringsLength);
+ _vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsData);
- _vm->loadStrings(_actorsStrings, stringsPointer, stringsLength);
- free(stringsPointer);
+ _vm->loadStrings(_actorsStrings, stringsData);
}
if (_vm->getGameId() == GID_ITE) {
- _actorsCount = ITE_ACTORCOUNT;
- _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i] = new ActorData();
- actor->_id = actorIndexToId(i);
+ _actors.resize(ITE_ACTORCOUNT);
+ i = 0;
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
actor->_index = i;
+ actor->_id = actorIndexToId(actor->_index);
debug(9, "init actor id=%d index=%d", actor->_id, actor->_index);
actor->_nameIndex = ITE_ActorTable[i].nameIndex;
actor->_scriptEntrypointNumber = ITE_ActorTable[i].scriptEntrypointNumber;
@@ -294,12 +282,11 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
warning("Disabling actor Id=%d index=%d", actor->_id, actor->_index);
}
}
- _objsCount = ITE_OBJECTCOUNT;
- _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
- for (i = 0; i < _objsCount; i++) {
- obj = _objs[i] = new ObjectData();
- obj->_id = objIndexToId(i);
+ _objs.resize(ITE_OBJECTCOUNT);
+ i = 0;
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj, i++) {
obj->_index = i;
+ obj->_id = objIndexToId(obj->_index);
debug(9, "init obj id=%d index=%d", obj->_id, obj->_index);
obj->_nameIndex = ITE_ObjectTable[i].nameIndex;
obj->_scriptEntrypointNumber = ITE_ObjectTable[i].scriptEntrypointNumber;
@@ -318,69 +305,42 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
Actor::~Actor() {
debug(9, "Actor::~Actor()");
-
- free(_pathCell);
- _actorsStrings.freeMem();
- //release resources
- freeProtagStates();
- freeActorList();
- freeObjList();
-}
-
-void Actor::freeProtagStates() {
- int i;
- for (i = 0; i < _protagStatesCount; i++) {
- free(_protagStates[i]._frames);
- }
- free(_protagStates);
- _protagStates = NULL;
- _protagStatesCount = 0;
}
-void Actor::loadFrameList(int frameListResourceId, ActorFrameSequence *&framesPointer, int &framesCount) {
- byte *resourcePointer;
- size_t resourceLength;
+void Actor::loadFrameList(int frameListResourceId, ActorFrameSequences &frames) {
+ ByteArray resourceData;
debug(9, "Loading frame resource id %d", frameListResourceId);
- _vm->_resource->loadResource(_actorContext, frameListResourceId, resourcePointer, resourceLength);
-
- framesCount = resourceLength / 16;
- debug(9, "Frame resource contains %d frames (res length is %d)", framesCount, (int)resourceLength);
+ _vm->_resource->loadResource(_actorContext, frameListResourceId, resourceData);
- framesPointer = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * framesCount);
- if (framesPointer == NULL && framesCount != 0) {
- memoryError("Actor::loadFrameList");
- }
+ frames.resize(resourceData.size() / 16);
+ debug(9, "Frame resource contains %d frames (res length is %d)", frames.size(), (int)resourceData.size());
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _actorContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _actorContext->isBigEndian());
- for (int i = 0; i < framesCount; i++) {
- debug(9, "frameType %d", i);
+ for (ActorFrameSequences::iterator frame = frames.begin(); frame != frames.end(); ++frame) {
for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
// Load all four orientations
- framesPointer[i].directions[orient].frameIndex = readS.readUint16();
+ frame->directions[orient].frameIndex = readS.readUint16();
if (_vm->getGameId() == GID_ITE) {
- framesPointer[i].directions[orient].frameCount = readS.readSint16();
+ frame->directions[orient].frameCount = readS.readSint16();
} else {
- framesPointer[i].directions[orient].frameCount = readS.readByte();
+ frame->directions[orient].frameCount = readS.readByte();
readS.readByte();
}
- if (framesPointer[i].directions[orient].frameCount < 0)
- warning("frameCount < 0 (%d)", framesPointer[i].directions[orient].frameCount);
- debug(9, "frameIndex %d frameCount %d", framesPointer[i].directions[orient].frameIndex, framesPointer[i].directions[orient].frameCount);
+ if (frame->directions[orient].frameCount < 0)
+ warning("frameCount < 0 (%d)", frame->directions[orient].frameCount);
+ debug(9, "frameIndex %d frameCount %d", frame->directions[orient].frameIndex, frame->directions[orient].frameCount);
}
}
-
- free(resourcePointer);
}
bool Actor::loadActorResources(ActorData *actor) {
bool gotSomething = false;
if (actor->_frameListResourceId) {
- loadFrameList(actor->_frameListResourceId, actor->_frames, actor->_framesCount);
-
- actor->_shareFrames = false;
+ loadFrameList(actor->_frameListResourceId, actor->_framesContainer);
+ actor->_frames = &actor->_framesContainer;
gotSomething = true;
} else {
@@ -400,26 +360,18 @@ bool Actor::loadActorResources(ActorData *actor) {
return gotSomething;
}
-void Actor::freeActorList() {
- int i;
- ActorData *actor;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
- delete actor;
- }
- free(_actors);
- _actors = NULL;
- _actorsCount = 0;
-}
-
void Actor::loadActorSpriteList(ActorData *actor) {
- int lastFrame = 0;
+ uint lastFrame = 0;
+ uint curFrameIndex;
int resourceId = actor->_spriteListResourceId;
-
- for (int i = 0; i < actor->_framesCount; i++) {
- for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
- if (actor->_frames[i].directions[orient].frameIndex > lastFrame) {
- lastFrame = actor->_frames[i].directions[orient].frameIndex;
+
+ if (actor->_frames != NULL) {
+ for (ActorFrameSequences::const_iterator i = actor->_frames->begin(); i != actor->_frames->end(); ++i) {
+ for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
+ curFrameIndex = i->directions[orient].frameIndex;
+ if (curFrameIndex > lastFrame) {
+ lastFrame = curFrameIndex;
+ }
}
}
}
@@ -430,7 +382,7 @@ void Actor::loadActorSpriteList(ActorData *actor) {
if (_vm->getGameId() == GID_ITE) {
if (actor->_flags & kExtended) {
- while ((lastFrame >= actor->_spriteList.spriteCount)) {
+ while ((lastFrame >= actor->_spriteList.size())) {
resourceId++;
debug(9, "Appending to actor sprite list %d", resourceId);
_vm->_sprite->loadList(resourceId, actor->_spriteList);
@@ -441,9 +393,7 @@ void Actor::loadActorSpriteList(ActorData *actor) {
void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResourceID, int protagStatesCount, int protagStatesResourceID) {
int i, j;
- ActorData *actor;
- byte* actorListData;
- size_t actorListLength;
+ ByteArray actorListData;
byte walk[128];
byte acv[6];
int movementSpeed;
@@ -451,24 +401,20 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
int walkStepCount;
int stateResourceId;
- freeActorList();
-
- _vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData, actorListLength);
+ _vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData);
- _actorsCount = actorCount;
-
- if (actorListLength != (uint)_actorsCount * ACTOR_INHM_SIZE) {
+ if (actorListData.size() != (uint)actorCount * ACTOR_INHM_SIZE) {
error("Actor::loadActorList wrong actorlist length");
}
- MemoryReadStream actorS(actorListData, actorListLength);
+ ByteArrayReadStreamEndian actorS(actorListData);
- _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i] = new ActorData();
- actor->_id = objectIndexToId(kGameObjectActor, i); //actorIndexToId(i);
+ _actors.resize(actorCount);
+ i = 0;
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
actor->_index = i;
- debug(4, "init actor id=0x%x index=%d", actor->_id, actor->_index);
+ actor->_id = objectIndexToId(kGameObjectActor, actor->_index); //actorIndexToId(i);
+ debug(4, "init actor id=0x%X index=%d", actor->_id, actor->_index);
actorS.readUint32LE(); //next displayed
actorS.readByte(); //type
actor->_flags = actorS.readByte();
@@ -531,86 +477,58 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
}
// actorS.seek(6, SEEK_CUR); //action vars
}
- free(actorListData);
- _actors[protagonistIdx]->_flags |= kProtagonist | kExtended;
+ _actors[protagonistIdx]._flags |= kProtagonist | kExtended;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
//if (actor->_flags & kProtagonist) {
loadActorResources(actor);
//break;
//}
}
- _centerActor = _protagonist = _actors[protagonistIdx];
+ _centerActor = _protagonist = &_actors[protagonistIdx];
_protagState = 0;
if (protagStatesResourceID) {
- if (!_protagonist->_shareFrames)
- free(_protagonist->_frames);
- freeProtagStates();
-
- _protagStates = (ProtagStateData *)malloc(sizeof(ProtagStateData) * protagStatesCount);
+ _protagStates.resize(protagStatesCount);
- byte *idsResourcePointer;
- size_t idsResourceLength;
+ ByteArray idsResourceData;
- _vm->_resource->loadResource(_actorContext, protagStatesResourceID,
- idsResourcePointer, idsResourceLength);
+ _vm->_resource->loadResource(_actorContext, protagStatesResourceID, idsResourceData);
- if (idsResourceLength < (size_t)protagStatesCount * 4) {
+ if (idsResourceData.size() < (size_t)protagStatesCount * 4) {
error("Wrong protagonist states resource");
}
- MemoryReadStream statesIds(idsResourcePointer, idsResourceLength);
+ ByteArrayReadStreamEndian statesIds(idsResourceData);
for (i = 0; i < protagStatesCount; i++) {
stateResourceId = statesIds.readUint32LE();
- loadFrameList(stateResourceId, _protagStates[i]._frames, _protagStates[i]._framesCount);
+ loadFrameList(stateResourceId, _protagStates[i]._frames);
}
- free(idsResourcePointer);
- _protagonist->_frames = _protagStates[_protagState]._frames;
- _protagonist->_framesCount = _protagStates[_protagState]._framesCount;
- _protagonist->_shareFrames = true;
+ _protagonist->_frames = &_protagStates[_protagState]._frames;
}
- _protagStatesCount = protagStatesCount;
-}
-
-void Actor::freeObjList() {
- int i;
- ObjectData *object;
- for (i = 0; i < _objsCount; i++) {
- object = _objs[i];
- delete object;
- }
- free(_objs);
- _objs = NULL;
- _objsCount = 0;
}
void Actor::loadObjList(int objectCount, int objectsResourceID) {
- int i;
+ uint i;
int frameListResourceId;
- ObjectData *object;
- byte* objectListData;
- size_t objectListLength;
- freeObjList();
+ ByteArray objectListData;
- _vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData, objectListLength);
+ _vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData);
- _objsCount = objectCount;
+ _objs.resize(objectCount);
- MemoryReadStream objectS(objectListData, objectListLength);
+ ByteArrayReadStreamEndian objectS(objectListData);
- _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
- for (i = 0; i < _objsCount; i++) {
- object = _objs[i] = new ObjectData();
- object->_id = objectIndexToId(kGameObjectObject, i);
+ i = 0;
+ for (ObjectDataArray::iterator object = _objs.begin(); object != _objs.end(); ++object, i++) {
object->_index = i;
+ object->_id = objectIndexToId(kGameObjectObject, object->_index);
debug(9, "init object id=%d index=%d", object->_id, object->_index);
objectS.readUint32LE(); //next displayed
objectS.readByte(); //type
@@ -635,7 +553,6 @@ void Actor::loadObjList(int objectCount, int objectsResourceID) {
objectS.readUint16LE(); //BOTTOM
object->_interactBits = objectS.readUint16LE();
}
- free(objectListData);
}
void Actor::takeExit(uint16 actorId, const HitZone *hitZone) {
@@ -683,7 +600,7 @@ void Actor::stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit,
event.param5 = ID_NOTHING; // With Object
event.param6 = ID_PROTAG; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
@@ -693,7 +610,7 @@ ObjectData *Actor::getObj(uint16 objId) {
if (!validObjId(objId))
error("Actor::getObj Wrong objId 0x%X", objId);
- obj = _objs[objIdToIndex(objId)];
+ obj = &_objs[objIdToIndex(objId)];
if (obj->_disabled)
error("Actor::getObj disabled objId 0x%X", objId);
@@ -716,7 +633,7 @@ ActorData *Actor::getActor(uint16 actorId) {
return _protagonist;
}
- actor = _actors[actorIdToIndex(actorId)];
+ actor = &_actors[actorIdToIndex(actorId)];
if (actor->_disabled)
error("Actor::getActor disabled actorId 0x%X", actorId);
@@ -729,12 +646,8 @@ void Actor::setProtagState(int state) {
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
- if (!_protagonist->_shareFrames)
- free(_protagonist->_frames);
- _protagonist->_frames = _protagStates[state]._frames;
- _protagonist->_framesCount = _protagStates[state]._framesCount;
- _protagonist->_shareFrames = true;
+ _protagonist->_frames = &_protagStates[state]._frames;
}
#endif
@@ -797,15 +710,18 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
if ((actor->_facingDirection < kDirUp) || (actor->_facingDirection > kDirUpLeft))
error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->_facingDirection, actorId);
+ ActorFrameSequences *frames;
+ frames = actor->_frames;
+
if (_vm->getGameId() == GID_ITE) {
- if (frameType >= actor->_framesCount) {
- warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
+ if (uint(frameType) >= frames->size()) {
+ warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, frames->size(), actorId);
return &def;
}
fourDirection = actorDirectionsLUT[actor->_facingDirection];
- return &actor->_frames[frameType].directions[fourDirection];
+ return &(*frames)[frameType].directions[fourDirection];
}
#ifdef ENABLE_IHNM
@@ -816,12 +732,12 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
// Both of them are invisible and immovable
// There is no point to keep throwing warnings about this, the original checks for
// a valid framecount too
- if (actor->_framesCount == 0) {
+ if ((frames == NULL) || (frames->empty())) {
return &def;
}
- frameType = CLIP(frameType, 0, actor->_framesCount - 1);
+ frameType = CLIP(frameType, 0, int(frames->size() - 1));
fourDirection = actorDirectionsLUT[actor->_facingDirection];
- return &actor->_frames[frameType].directions[fourDirection];
+ return &(*frames)[frameType].directions[fourDirection];
}
#endif
@@ -1085,9 +1001,6 @@ void Actor::drawOrderListAdd(const CommonObjectDataPointer& element, CompareFunc
}
void Actor::createDrawOrderList() {
- int i;
- ActorData *actor;
- ObjectData *obj;
CompareFunction compareFunction = 0;
if (_vm->_scene->getFlags() & kSceneFlagISO) {
@@ -1102,8 +1015,7 @@ void Actor::createDrawOrderList() {
}
_drawOrderList.clear();
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (!actor->_inScene)
continue;
@@ -1113,8 +1025,7 @@ void Actor::createDrawOrderList() {
}
}
- for (i = 0; i < _objsCount; i++) {
- obj = _objs[i];
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
if (obj->_disabled)
continue;
@@ -1146,19 +1057,20 @@ bool Actor::getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber
ActorData *actor = (ActorData *)commonObjectData;
spriteList = &(actor->_spriteList);
frameNumber = actor->_frameNumber;
- if (spriteList->infoList == NULL)
+ if (spriteList->empty()) {
loadActorSpriteList(actor);
+ }
} else if (validObjId(commonObjectData->_id)) {
spriteList = &_vm->_sprite->_mainSprites;
frameNumber = commonObjectData->_spriteListResourceId;
}
- if (spriteList->spriteCount == 0) {
+ if (spriteList->empty()) {
return false;
}
- if ((frameNumber < 0) || (spriteList->spriteCount <= frameNumber)) {
+ if ((frameNumber < 0) || (spriteList->size() <= uint(frameNumber))) {
debug(1, "Actor::getSpriteParams frameNumber invalid for %s id 0x%X (%d)",
validObjId(commonObjectData->_id) ? "object" : "actor",
commonObjectData->_id, frameNumber);
@@ -1184,7 +1096,7 @@ void Actor::drawActors() {
return;
}
- if (_vm->_scene->_entryList.entryListCount == 0) {
+ if (_vm->_scene->_entryList.empty()) {
return;
}
@@ -1222,12 +1134,13 @@ void Actor::drawSpeech() {
ActorData *actor;
int width, height;
int stringLength = strlen(_activeSpeech.strings[0]);
- char *outputString = (char*)calloc(stringLength + 1, 1);
+ Common::Array<char> outputString;
+ outputString.resize(stringLength + 1);
if (_activeSpeech.speechFlags & kSpeakSlow)
- strncpy(outputString, _activeSpeech.strings[0], _activeSpeech.slowModeCharIndex + 1);
+ strncpy(&outputString.front(), _activeSpeech.strings[0], _activeSpeech.slowModeCharIndex + 1);
else
- strncpy(outputString, _activeSpeech.strings[0], stringLength);
+ strncpy(&outputString.front(), _activeSpeech.strings[0], stringLength);
if (_activeSpeech.actorsCount > 1) {
height = _vm->_font->getHeight(kKnownFontScript);
@@ -1244,15 +1157,13 @@ void Actor::drawSpeech() {
else if (_vm->getGameId() == GID_IHNM)
textPoint.y = 10; // CLIP(actor->_screenPosition.y - 160, 10, _vm->_scene->getHeight(true) - 10 - height);
- _vm->_font->textDraw(kKnownFontScript, outputString, textPoint,
+ _vm->_font->textDraw(kKnownFontScript, &outputString.front(), textPoint,
_activeSpeech.speechColor[i], _activeSpeech.outlineColor[i], _activeSpeech.getFontFlags(i));
}
} else {
- _vm->_font->textDrawRect(kKnownFontScript, outputString, _activeSpeech.drawRect, _activeSpeech.speechColor[0],
+ _vm->_font->textDrawRect(kKnownFontScript, &outputString.front(), _activeSpeech.drawRect, _activeSpeech.speechColor[0],
_activeSpeech.outlineColor[0], _activeSpeech.getFontFlags(0));
}
-
- free(outputString);
}
void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
@@ -1302,9 +1213,9 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
// Check Script::sfDropObject for the other part of this hack
if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 3 &&
_vm->_scene->currentSceneNumber() == 59 && _activeSpeech.sampleResourceId == 286) {
- for (i = 0; i < _objsCount; i++) {
- if (_objs[i]->_id == 16385) { // the compact disk
- _objs[i]->_sceneNumber = 59;
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ if (obj->_id == 16385) { // the compact disk
+ obj->_sceneNumber = 59;
break;
}
}
@@ -1376,36 +1287,31 @@ void Actor::abortSpeech() {
}
void Actor::saveState(Common::OutSaveFile *out) {
- uint16 i;
out->writeSint16LE(getProtagState());
- for (i = 0; i < _actorsCount; i++) {
- ActorData *a = _actors[i];
- a->saveState(out);
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
+ actor->saveState(out);
}
- for (i = 0; i < _objsCount; i++) {
- ObjectData *o = _objs[i];
- o->saveState(out);
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ obj->saveState(out);
}
}
void Actor::loadState(Common::InSaveFile *in) {
- int32 i;
int16 protagState = in->readSint16LE();
- if (protagState != 0 || _protagonist->_shareFrames)
+ if (protagState != 0 || (_protagonist->shareFrames())) {
setProtagState(protagState);
+ }
- for (i = 0; i < _actorsCount; i++) {
- ActorData *a = _actors[i];
- a->loadState(_vm->getCurrentLoadVersion(), in);
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
+ actor->loadState(_vm->getCurrentLoadVersion(), in);
}
- for (i = 0; i < _objsCount; i++) {
- ObjectData *o = _objs[i];
- o->loadState(in);
+ for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
+ obj->loadState(in);
}
}
diff --git a/engines/saga/actor.h b/engines/saga/actor.h
index 57d06e9e3a..2b6b5c2a3b 100644
--- a/engines/saga/actor.h
+++ b/engines/saga/actor.h
@@ -196,6 +196,8 @@ struct ActorFrameSequence {
ActorFrameRange directions[ACTOR_DIRECTIONS_COUNT];
};
+typedef Common::Array<ActorFrameSequence> ActorFrameSequences;
+
uint pathLine(PointList &pointList, uint idx, const Point &point1, const Point &point2);
struct Location {
@@ -323,6 +325,21 @@ public:
_screenDepth = in->readSint32LE();
_screenScale = in->readSint32LE();
}
+
+ CommonObjectData() {
+ _disabled = false;
+ _index = 0;
+ _id = 0;
+ _scriptEntrypointNumber = 0;
+
+ _flags = 0;
+ _nameIndex = 0;
+ _sceneNumber = 0;
+ _spriteListResourceId = 0;
+
+ _screenDepth = 0;
+ _screenScale = 0;
+ }
};
typedef CommonObjectData *CommonObjectDataPointer;
@@ -333,19 +350,21 @@ class ObjectData: public CommonObjectData {
public:
//constant
uint16 _interactBits;
+
ObjectData() {
- memset(this, 0, sizeof(*this));
+ _interactBits = 0;
}
};
+typedef Common::Array<ObjectData> ObjectDataArray;
+
class ActorData: public CommonObjectData {
public:
//constant
SpriteList _spriteList; // sprite list data
- bool _shareFrames;
- ActorFrameSequence *_frames; // Actor's frames
- int _framesCount; // Actor's frames count
+ ActorFrameSequences *_frames; // Actor's frames
+ ActorFrameSequences _framesContainer; // Actor's frames
int _frameListResourceId; // Actor's frame list resource id
byte _speechColor; // Actor dialogue color
@@ -376,11 +395,9 @@ public:
int32 _frameNumber; // current frame number
- int32 _tileDirectionsAlloced;
- byte *_tileDirections;
+ ByteArray _tileDirections;
- int32 _walkStepsAlloced;
- Point *_walkStepsPoints;
+ Common::Array<Point> _walkStepsPoints;
int32 _walkStepsCount;
int32 _walkStepIndex;
@@ -391,21 +408,21 @@ public:
public:
ActorData();
- ~ActorData();
void saveState(Common::OutSaveFile *out);
void loadState(uint32 version, Common::InSaveFile *in);
- void setTileDirectionsSize(int size, bool forceRealloc);
void cycleWrap(int cycleLimit);
- void setWalkStepsPointsSize(int size, bool forceRealloc);
void addWalkStepPoint(const Point &point);
- void freeSpriteList();
+ bool shareFrames() {
+ return ((_frames != NULL) && (_frames != &_framesContainer));
+ }
};
+typedef Common::Array<ActorData> ActorDataArray;
+
struct ProtagStateData {
- ActorFrameSequence *_frames; // Actor's frames
- int _framesCount; // Actor's frames count
+ ActorFrameSequences _frames; // Actor's frames
};
@@ -450,15 +467,17 @@ 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); }
+ bool validActorId(uint16 id) {
+ return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actors.size())));
+ }
+ 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]; }
+ ActorData *getFirstActor() { return &_actors.front(); }
// clarification: Obj - means game object, such Hat, Spoon etc, Object - means Actor,Obj,HitZone,StepZone
- bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objsCount)); }
+ bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objs.size())); }
int objIdToIndex(uint16 id) { return objectIdToIndex(id); }
uint16 objIndexToId(int index) { return objectIndexToId(kGameObjectObject, index); }
ObjectData *getObj(uint16 objId);
@@ -525,18 +544,14 @@ public:
void setProtagState(int state);
int getProtagState() { return _protagState; }
- void freeProtagStates();
-
- void freeActorList();
void loadActorList(int protagonistIdx, int actorCount, int actorsResourceID,
int protagStatesCount, int protagStatesResourceID);
- void freeObjList();
void loadObjList(int objectCount, int objectsResourceID);
protected:
friend class Script;
bool loadActorResources(ActorData *actor);
- void loadFrameList(int frameListResourceId, ActorFrameSequence *&framesPointer, int &framesCount);
+ void loadFrameList(int frameListResourceId, ActorFrameSequences &frames);
private:
void stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped);
void loadActorSpriteList(ActorData *actor);
@@ -584,11 +599,9 @@ private:
protected:
//constants
- int _actorsCount;
- ActorData **_actors;
+ ActorDataArray _actors;
- int _objsCount;
- ObjectData **_objs;
+ ObjectDataArray _objs;
SagaEngine *_vm;
ResourceContext *_actorContext;
@@ -613,8 +626,7 @@ protected:
bool _dragonHunt;
private:
- ProtagStateData *_protagStates;
- int _protagStatesCount;
+ Common::Array<ProtagStateData> _protagStates;
//path stuff
struct PathNode {
@@ -629,7 +641,7 @@ private:
Rect _barrierList[ACTOR_BARRIERS_MAX];
int _barrierCount;
- int8 *_pathCell;
+ Common::Array<int8> _pathCell;
int _xCellCount;
int _yCellCount;
diff --git a/engines/saga/actor_walk.cpp b/engines/saga/actor_walk.cpp
index 21643ac1de..5a8ea0c856 100644
--- a/engines/saga/actor_walk.cpp
+++ b/engines/saga/actor_walk.cpp
@@ -179,9 +179,8 @@ void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) {
}
void Actor::updateActorsScene(int actorsEntrance) {
- int i, j;
+ int j;
int followerDirection;
- ActorData *actor;
Location tempLocation;
Location possibleLocation;
Point delta;
@@ -196,14 +195,13 @@ void Actor::updateActorsScene(int actorsEntrance) {
_activeSpeech.playing = false;
_protagonist = NULL;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
actor->_inScene = false;
- actor->_spriteList.freeMem();
+ actor->_spriteList.clear();
if (actor->_disabled) {
continue;
}
- if ((actor->_flags & (kProtagonist | kFollower)) || (i == 0)) {
+ if ((actor->_flags & (kProtagonist | kFollower)) || (actor->_index == 0)) {
if (actor->_flags & kProtagonist) {
actor->_finalTarget = actor->_location;
_centerActor = _protagonist = actor;
@@ -227,12 +225,12 @@ void Actor::updateActorsScene(int actorsEntrance) {
if (_protagonist == NULL)
return;
- if ((actorsEntrance >= 0) && (_vm->_scene->_entryList.entryListCount > 0)) {
- if (_vm->_scene->_entryList.entryListCount <= actorsEntrance) {
+ if ((actorsEntrance >= 0) && (!_vm->_scene->_entryList.empty())) {
+ if (_vm->_scene->_entryList.size() <= uint(actorsEntrance)) {
actorsEntrance = 0; //OCEAN bug
}
- sceneEntry = _vm->_scene->_entryList.getEntry(actorsEntrance);
+ sceneEntry = &_vm->_scene->_entryList[actorsEntrance];
if (_vm->_scene->getFlags() & kSceneFlagISO) {
_protagonist->_location = sceneEntry->location;
} else {
@@ -266,8 +264,7 @@ void Actor::updateActorsScene(int actorsEntrance) {
followerDirection = _protagonist->_facingDirection + 3;
calcScreenPosition(_protagonist);
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (actor->_flags & (kFollower)) {
actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection;
actor->_currentAction = kActionWait;
@@ -323,8 +320,6 @@ void Actor::updateActorsScene(int actorsEntrance) {
}
void Actor::handleActions(int msec, bool setup) {
- int i;
- ActorData *actor;
ActorFrameRange *frameRange;
int state;
int speed;
@@ -336,12 +331,11 @@ void Actor::handleActions(int msec, bool setup) {
Point hitPoint;
Location pickLocation;
- for (i = 0; i < _actorsCount; i++) {
- actor = _actors[i];
+ for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
if (!actor->_inScene)
continue;
- if ((_vm->getGameId() == GID_ITE) && (i == ACTOR_DRAGON_INDEX)) {
+ if ((_vm->getGameId() == GID_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) {
moveDragon(actor);
continue;
}
@@ -722,7 +716,7 @@ void Actor::handleActions(int msec, bool setup) {
void Actor::direct(int msec) {
- if (_vm->_scene->_entryList.entryListCount == 0) {
+ if (_vm->_scene->_entryList.empty()) {
return;
}
@@ -866,8 +860,6 @@ bool Actor::followProtagonist(ActorData *actor) {
bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
ActorData *actor;
- ActorData *anotherActor;
- int i;
Rect testBox;
Rect testBox2;
@@ -943,7 +935,7 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
int max = _vm->getGameId() == GID_ITE ? 8 : 4;
- for (i = 1; i < max; i++) {
+ for (int i = 1; i < max; i++) {
pointAdd = pointFrom;
pointAdd.y += i;
if (_vm->_scene->canWalk(pointAdd)) {
@@ -978,9 +970,7 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2);
collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2);
-
- for (i = 0; (i < _actorsCount) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) {
- anotherActor = _actors[i];
+ for (ActorDataArray::iterator anotherActor = _actors.begin(); (anotherActor != _actors.end()) && (_barrierCount < ACTOR_BARRIERS_MAX); ++anotherActor) {
if (!anotherActor->_inScene)
continue;
if (anotherActor == actor)
@@ -1067,8 +1057,8 @@ bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
return false;
} else {
if (actor->_flags & kProtagonist) {
- _actors[1]->_actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
- _actors[2]->_actorFlags &= ~kActorNoFollow;
+ _actors[1]._actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
+ _actors[2]._actorFlags &= ~kActorNoFollow;
}
actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint;
actor->_walkFrameSequence = getFrameType(kFrameWalk);
@@ -1153,7 +1143,7 @@ void Actor::moveDragon(ActorData *actor) {
event.param4 = -1; // Object
event.param5 = -1; // With Object
event.param6 = -1; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_dragonHunt = false;
}
diff --git a/engines/saga/animation.cpp b/engines/saga/animation.cpp
index 0d65d2f191..aca29ed82e 100644
--- a/engines/saga/animation.cpp
+++ b/engines/saga/animation.cpp
@@ -42,8 +42,6 @@ namespace Saga {
Anim::Anim(SagaEngine *vm) : _vm(vm) {
uint16 i;
- _cutawayList = NULL;
- _cutawayListLength = 0;
_cutawayActive = false;
for (i = 0; i < MAX_ANIMATIONS; i++)
@@ -55,21 +53,16 @@ Anim::Anim(SagaEngine *vm) : _vm(vm) {
Anim::~Anim() {
reset();
-#ifdef ENABLE_IHNM
- freeCutawayList();
-#endif
}
#ifdef ENABLE_IHNM
-void Anim::loadCutawayList(const byte *resourcePointer, size_t resourceLength) {
- free(_cutawayList);
- _cutawayListLength = resourceLength / 8;
- _cutawayList = (Cutaway *)malloc(_cutawayListLength * sizeof(Cutaway));
+void Anim::loadCutawayList(const ByteArray &resourceData) {
+ _cutawayList.resize(resourceData.size() / 8);
- MemoryReadStream cutawayS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian cutawayS(resourceData);
- for (int i = 0; i < _cutawayListLength; i++) {
+ for (uint i = 0; i < _cutawayList.size(); i++) {
_cutawayList[i].backgroundResourceId = cutawayS.readUint16LE();
_cutawayList[i].animResourceId = cutawayS.readUint16LE();
_cutawayList[i].cycles = cutawayS.readSint16LE();
@@ -77,20 +70,16 @@ void Anim::loadCutawayList(const byte *resourcePointer, size_t resourceLength) {
}
}
-void Anim::freeCutawayList() {
- free(_cutawayList);
- _cutawayList = NULL;
- _cutawayListLength = 0;
+void Anim::clearCutawayList() {
+ _cutawayList.clear();
}
int Anim::playCutaway(int cut, bool fade) {
debug(0, "playCutaway(%d, %d)", cut, fade);
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
bool startImmediately = false;
- byte *resourceData;
- size_t resourceDataLength;
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
_cutAwayFade = fade;
@@ -111,7 +100,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -120,7 +109,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Prepare cutaway
@@ -148,7 +137,7 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = 0;
event.duration = 0;
event.param = _cutawayList[cut].backgroundResourceId;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
} else {
showCutawayBg(_cutawayList[cut].backgroundResourceId);
}
@@ -180,9 +169,10 @@ int Anim::playCutaway(int cut, bool fade) {
// for the second from the left monitor in Ellen's chapter etc
// Therefore, skip the animation bit if animResourceId is 0 and only show the background
if (_cutawayList[cut].animResourceId != 0) {
- _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData, resourceDataLength);
- load(MAX_ANIMATIONS + cutawaySlot, resourceData, resourceDataLength);
- free(resourceData);
+ ByteArray resourceData;
+ _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData);
+ load(MAX_ANIMATIONS + cutawaySlot, resourceData);
+
setCycles(MAX_ANIMATIONS + cutawaySlot, _cutawayList[cut].cycles);
setFrameTime(MAX_ANIMATIONS + cutawaySlot, 1000 / _cutawayList[cut].frameRate);
@@ -198,9 +188,9 @@ int Anim::playCutaway(int cut, bool fade) {
event.time = (40 / 3) * 1000 / _cutawayList[cut].frameRate;
if (fade)
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
else
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
return MAX_ANIMATIONS + cutawaySlot;
@@ -222,7 +212,7 @@ void Anim::returnFromCutaway() {
if (_cutawayActive) {
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
if (_cutAwayFade) {
static PalEntry cur_pal[PAL_ENTRIES];
@@ -237,7 +227,7 @@ void Anim::returnFromCutaway() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -246,7 +236,7 @@ void Anim::returnFromCutaway() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Clear the cutaway. Note that this sets _cutawayActive to false
@@ -257,9 +247,9 @@ void Anim::returnFromCutaway() {
event.duration = 0;
if (_cutAwayFade)
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ eventColumns = _vm->_events->chain(eventColumns, event); // chain with the other events
else
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_scene->restoreScene();
@@ -279,7 +269,7 @@ void Anim::returnFromCutaway() {
event.op = kEventResumeAll;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ _vm->_events->chain(eventColumns, event); // chain with the other events
// Draw the scene
event.type = kEvTImmediate;
@@ -287,7 +277,7 @@ void Anim::returnFromCutaway() {
event.op = kEventDraw;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event); // chain with the other events
+ _vm->_events->chain(eventColumns, event); // chain with the other events
// Handle fade up, if we previously faded down
if (_cutAwayFade) {
@@ -297,14 +287,14 @@ void Anim::returnFromCutaway() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = saved_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypeWakeUp;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
}
@@ -338,22 +328,20 @@ void Anim::clearCutaway() {
void Anim::showCutawayBg(int bg) {
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- byte *resourceData;
- size_t resourceDataLength;
- byte *buf;
- size_t buflen;
+ ByteArray resourceData;
+ ByteArray image;
int width;
int height;
Event event;
static PalEntry pal[PAL_ENTRIES];
- _vm->_resource->loadResource(context, bg, resourceData, resourceDataLength);
- _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height);
+ _vm->_resource->loadResource(context, bg, resourceData);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
- const byte *palPointer = _vm->getImagePal(resourceData, resourceDataLength);
+ const byte *palPointer = _vm->getImagePal(resourceData);
memcpy(pal, palPointer, sizeof(pal));
const Rect rect(width, height);
- _vm->_render->getBackGroundSurface()->blit(rect, buf);
+ _vm->_render->getBackGroundSurface()->blit(rect, image.getBuffer());
_vm->_render->setFullRefresh(true);
_vm->_frameCount++;
@@ -365,13 +353,10 @@ void Anim::showCutawayBg(int bg) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
_vm->_gfx->setPalette(pal);
}
-
- free(buf);
- free(resourceData);
}
void Anim::startVideo(int vid, bool fade) {
@@ -397,18 +382,18 @@ void Anim::returnFromVideo() {
#endif
-void Anim::load(uint16 animId, const byte *animResourceData, size_t animResourceLength) {
+void Anim::load(uint16 animId, const ByteArray &resourceData) {
AnimationData *anim;
uint16 temp;
if (animId >= MAX_ANIMATIONS) {
if (animId >= MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations))
error("Anim::load could not find unused animation slot");
- anim = _cutawayAnimations[animId - MAX_ANIMATIONS] = new AnimationData(animResourceData, animResourceLength);
+ anim = _cutawayAnimations[animId - MAX_ANIMATIONS] = new AnimationData();
} else
- anim = _animations[animId] = new AnimationData(animResourceData, animResourceLength);
+ anim = _animations[animId] = new AnimationData();
- MemoryReadStreamEndian headerReadS(anim->resourceData, anim->resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian headerReadS(resourceData, _vm->isBigEndian());
anim->magic = headerReadS.readUint16LE(); // cause ALWAYS LE
anim->screenWidth = headerReadS.readUint16();
anim->screenHeight = headerReadS.readUint16();
@@ -418,23 +403,30 @@ void Anim::load(uint16 animId, const byte *animResourceData, size_t animResource
anim->maxFrame = headerReadS.readByte() - 1;
anim->loopFrame = headerReadS.readByte() - 1;
temp = headerReadS.readUint16BE();
- anim->start = headerReadS.pos();
+ size_t start;
+
+ start = headerReadS.pos();
if (temp == (uint16)(-1)) {
temp = 0;
}
- anim->start += temp;
+ start += temp;
+ size_t dataOffset = headerReadS.pos();
+ if (dataOffset != start) {
+ warning("Anim::load animId=%d start != dataOffset 0x%X 0x%X", animId, uint(start), uint(dataOffset));
+ }
+
+ anim->resourceData.resize(resourceData.size() - dataOffset);
+
+ memcpy(anim->resourceData.getBuffer(), resourceData.getBuffer() + dataOffset, anim->resourceData.size());
// Cache frame offsets
// WORKAROUND: Cutaway with background resource ID 37 (loaded as cutaway #4) is ending credits.
// For some reason it has wrong number of frames specified in its header. So we calculate it here:
- if (animId > MAX_ANIMATIONS && _cutawayListLength > 4 && _cutawayList[4].backgroundResourceId == 37 && anim->maxFrame == 143)
+ if (animId > MAX_ANIMATIONS && _cutawayList.size() > 4 && _cutawayList[4].backgroundResourceId == 37 && anim->maxFrame == 143)
anim->maxFrame = fillFrameOffsets(anim, false);
- anim->frameOffsets = (size_t *)malloc((anim->maxFrame + 1) * sizeof(*anim->frameOffsets));
- if (anim->frameOffsets == NULL) {
- memoryError("Anim::load");
- }
+ anim->frameOffsets.resize(anim->maxFrame + 1);
fillFrameOffsets(anim);
@@ -504,7 +496,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = 10;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Nothing to render here (apart from the background, which is already rendered),
// so return
@@ -534,7 +526,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
return;
}
@@ -575,7 +567,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = anim->frameTime + vectorTime;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
return;
} else {
@@ -601,7 +593,7 @@ void Anim::play(uint16 animId, int vectorTime, bool playing) {
event.op = kEventFrame;
event.param = animId;
event.time = frameTime;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Anim::stop(uint16 animId) {
@@ -688,7 +680,7 @@ void Anim::decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_
error("decodeFrame() Buffer size inadequate");
}
- MemoryReadStream readS(anim->resourceData + frameOffset, anim->resourceLength - frameOffset);
+ MemoryReadStream readS(&anim->resourceData[frameOffset], anim->resourceData.size() - frameOffset);
// FIXME: This is thrown when the first video of the IHNM end sequence is shown (the "turn off screen"
// video), however the video is played correctly and the rest of the end sequence continues normally
@@ -825,9 +817,7 @@ int Anim::fillFrameOffsets(AnimationData *anim, bool reallyFill) {
int i;
bool longData = isLongData();
- MemoryReadStreamEndian readS(anim->resourceData, anim->resourceLength, !_vm->isBigEndian()); // RLE has inversion BE<>LE
-
- readS.seek(12);
+ MemoryReadStreamEndian readS(&anim->resourceData.front(), anim->resourceData.size(), !_vm->isBigEndian()); // RLE has inversion BE<>LE
while (readS.pos() != readS.size()) {
if (reallyFill) {
@@ -843,7 +833,7 @@ int Anim::fillFrameOffsets(AnimationData *anim, bool reallyFill) {
// including the frame header, is in big endian format
do {
markByte = readS.readByte();
-// debug(7, "_pos=%x currentFrame=%i markByte=%x", readS.pos(), currentFrame, markByte);
+// debug(7, "_pos=%X currentFrame=%i markByte=%X", readS.pos(), currentFrame, markByte);
switch (markByte) {
case SAGA_FRAME_START: // Start of frame
@@ -942,9 +932,9 @@ void Anim::animInfo() {
void Anim::cutawayInfo() {
uint16 i;
- _vm->_console->DebugPrintf("There are %d cutaways loaded:\n", _cutawayListLength);
+ _vm->_console->DebugPrintf("There are %d cutaways loaded:\n", _cutawayList.size());
- for (i = 0; i < _cutawayListLength; i++) {
+ for (i = 0; i < _cutawayList.size(); i++) {
_vm->_console->DebugPrintf("%02d: Bg res: %u Anim res: %u Cycles: %u Framerate: %u\n", i,
_cutawayList[i].backgroundResourceId, _cutawayList[i].animResourceId,
_cutawayList[i].cycles, _cutawayList[i].frameRate);
diff --git a/engines/saga/animation.h b/engines/saga/animation.h
index 72b145089c..c27909115e 100644
--- a/engines/saga/animation.h
+++ b/engines/saga/animation.h
@@ -66,8 +66,7 @@ struct Cutaway {
// Animation info array member
struct AnimationData {
- byte *resourceData;
- size_t resourceLength;
+ ByteArray resourceData;
uint16 magic;
@@ -80,10 +79,8 @@ struct AnimationData {
int16 maxFrame;
int16 loopFrame;
- int16 start;
-
int16 currentFrame;
- size_t *frameOffsets;
+ Common::Array<size_t> frameOffsets;
uint16 completed;
uint16 cycles;
@@ -93,17 +90,6 @@ struct AnimationData {
AnimationState state;
int16 linkId;
uint16 flags;
-
- AnimationData(const byte *animResourceData, size_t animResourceLength) {
- memset(this, 0, sizeof(*this));
- resourceLength = animResourceLength;
- resourceData = (byte*)malloc(animResourceLength);
- memcpy(resourceData, animResourceData, animResourceLength);
- }
- ~AnimationData() {
- free(frameOffsets);
- free(resourceData);
- }
};
class Anim {
@@ -111,8 +97,8 @@ public:
Anim(SagaEngine *vm);
~Anim();
- void loadCutawayList(const byte *resourcePointer, size_t resourceLength);
- void freeCutawayList();
+ void loadCutawayList(const ByteArray &resourceData);
+ void clearCutawayList();
int playCutaway(int cut, bool fade);
void endCutaway();
void returnFromCutaway();
@@ -123,7 +109,7 @@ public:
void endVideo();
void returnFromVideo();
- void load(uint16 animId, const byte *animResourceData, size_t animResourceLength);
+ void load(uint16 animId, const ByteArray &resourceData);
void freeId(uint16 animId);
void play(uint16 animId, int vectorTime, bool playing = true);
void link(int16 animId1, int16 animId2);
@@ -154,9 +140,9 @@ public:
bool hasCutaway() { return _cutawayActive; }
void setCutAwayMode(int mode) { _cutAwayMode = mode; }
- int cutawayListLength() { return _cutawayListLength; }
- int cutawayBgResourceID(int cutaway) { return _cutawayList[cutaway].backgroundResourceId; }
- int cutawayAnimResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; }
+// int cutawayListLength() { return _cutawayListLength; }
+// int cutawayBgResourceID(int cutaway) { return _cutawayList[cutaway].backgroundResourceId; }
+// int cutawayAnimResourceID(int cutaway) { return _cutawayList[cutaway].animResourceId; }
private:
void decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_t bufLength);
@@ -205,9 +191,8 @@ private:
SagaEngine *_vm;
AnimationData *_animations[MAX_ANIMATIONS];
AnimationData *_cutawayAnimations[2];
- Cutaway *_cutawayList;
+ Common::Array<Cutaway> _cutawayList;
PalEntry saved_pal[PAL_ENTRIES];
- int _cutawayListLength;
bool _cutawayActive;
int _cutAwayMode;
bool _cutAwayFade;
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 7913291527..e43f1ee5c7 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -56,9 +56,9 @@ struct SAGAGameDescription {
bool SagaEngine::isBigEndian() const { return isMacResources() && getGameId() == GID_ITE; }
bool SagaEngine::isMacResources() const { return (getPlatform() == Common::kPlatformMacintosh); }
-const GameResourceDescription *SagaEngine::getResourceDescription() { return _gameDescription->resourceDescription; }
+const GameResourceDescription *SagaEngine::getResourceDescription() const { return _gameDescription->resourceDescription; }
-const GameFontDescription *SagaEngine::getFontDescription(int index) {
+const GameFontDescription *SagaEngine::getFontDescription(int index) const {
assert(index < _gameDescription->fontsCount);
return &_gameDescription->fontDescriptions[index];
}
@@ -259,7 +259,7 @@ SaveStateDescriptor SagaMetaEngine::querySaveMetaInfos(const char *target, int s
version = SWAP_BYTES_32(version);
}
- debug(2, "Save version: %x", version);
+ debug(2, "Save version: 0x%X", version);
if (version < 4)
warning("This savegame is not endian-safe. There may be problems");
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
index 1f4091d07c..cf27ad7559 100644
--- a/engines/saga/events.cpp
+++ b/engines/saga/events.cpp
@@ -49,13 +49,12 @@ Events::Events(SagaEngine *vm) : _vm(vm) {
Events::~Events() {
debug(8, "Shutting down event subsystem...");
- freeList();
}
// Function to process event list once per frame.
// First advances event times, then processes each event with the appropriate
// handler depending on the type of event.
-int Events::handleEvents(long msec) {
+void Events::handleEvents(long msec) {
long delta_time;
int result;
@@ -64,7 +63,7 @@ int Events::handleEvents(long msec) {
// Process each event in list
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
- Event *event_p = &*eventi;
+ Event *event_p = &eventi->front();
// Call the appropriate event handler for the specific event type
switch (event_p->type) {
@@ -95,17 +94,15 @@ int Events::handleEvents(long msec) {
// handler
if ((result == kEvStDelete) || (result == kEvStInvalidCode)) {
// If there is no event chain, delete the base event.
- if (event_p->chain == NULL) {
+ if (eventi->size() < 2) {
eventi = _eventList.reverse_erase(eventi);
} else {
// If there is an event chain present, move the next event
// in the chain up, adjust it by the previous delta time,
// and reprocess the event
delta_time = event_p->time;
- Event *from_chain = event_p->chain;
- memcpy(event_p, from_chain, sizeof(*event_p));
- free(from_chain);
-
+ eventi->pop_front();
+ event_p = &eventi->front();
event_p->time += delta_time;
--eventi;
}
@@ -113,8 +110,6 @@ int Events::handleEvents(long msec) {
break;
}
}
-
- return SUCCESS;
}
int Events::handleContinuous(Event *event) {
@@ -177,9 +172,8 @@ int Events::handleContinuous(Event *event) {
// set flag of Dissolve to 1. It is a hack to simulate zero masking.
int w, h;
byte *maskBuffer;
- size_t len;
- _vm->_scene->getBGMaskInfo(w, h, maskBuffer, len);
+ _vm->_scene->getBGMaskInfo(w, h, maskBuffer);
rect.left = (_vm->getDisplayInfo().width - w) / 2;
rect.top = (_vm->getDisplayInfo().height - h) / 2;
rect.setWidth(w);
@@ -362,31 +356,26 @@ int Events::handleOneShot(Event *event) {
{
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- byte *resourceData;
- size_t resourceDataLength;
+ ByteArray resourceData;
- _vm->_resource->loadResource(context, _vm->getResourceDescription()->psychicProfileResourceId, resourceData, resourceDataLength);
+ _vm->_resource->loadResource(context, _vm->getResourceDescription()->psychicProfileResourceId, resourceData);
- byte *buf;
- size_t buflen;
+ ByteArray image;
int width;
int height;
- _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
- const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData, resourceDataLength);
+ const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData);
const Rect profileRect(width, height);
- _vm->_render->getBackGroundSurface()->blit(profileRect, buf);
+ _vm->_render->getBackGroundSurface()->blit(profileRect, image.getBuffer());
_vm->_render->addDirtyRect(profileRect);
_vm->_frameCount++;
_vm->_gfx->setPalette(palette);
- free(buf);
- free(resourceData);
-
// Draw the scene. It won't be drawn by Render::drawScene(), as a placard is up
_vm->_scene->draw();
}
@@ -570,122 +559,74 @@ int Events::handleInterval(Event *event) {
return kEvStDelete;
}
-// Schedules an event in the event list; returns a pointer to the scheduled
-// event suitable for chaining if desired.
-Event *Events::queue(Event *event) {
- Event *queuedEvent;
-
- _eventList.push_back(*event);
- queuedEvent = &*--_eventList.end();
- initializeEvent(queuedEvent);
-
- return queuedEvent;
-}
-
-// Places a 'add_event' on the end of an event chain given by 'head_event'
-// (head_event may be in any position in the event chain)
-Event *Events::chain(Event *headEvent, Event *addEvent) {
- if (headEvent == NULL) {
- return queue(addEvent);
- }
+EventColumns *Events::chain(EventColumns *eventColumns, const Event &event) {
+
+ if (eventColumns == NULL) {
+ EventColumns tmp;
- Event *walkEvent;
- for (walkEvent = headEvent; walkEvent->chain != NULL; walkEvent = walkEvent->chain) {
- continue;
+ _eventList.push_back(tmp);
+ eventColumns = &_eventList.back();
}
- walkEvent->chain = (Event *)malloc(sizeof(*walkEvent->chain));
- *walkEvent->chain = *addEvent;
- initializeEvent(walkEvent->chain);
+ eventColumns->push_back(event);
+ initializeEvent(eventColumns->back());
- return walkEvent->chain;
+ return eventColumns;
}
-int Events::initializeEvent(Event *event) {
- event->chain = NULL;
- switch (event->type) {
+void Events::initializeEvent(Event &event) {
+ switch (event.type) {
case kEvTOneshot:
break;
case kEvTContinuous:
case kEvTImmediate:
- event->time += event->duration;
+ event.time += event.duration;
break;
case kEvTInterval:
break;
- default:
- return FAILURE;
}
-
- return SUCCESS;
}
-int Events::clearList(bool playQueuedMusic) {
- Event *chain_walk;
- Event *next_chain;
-
+void Events::clearList(bool playQueuedMusic) {
// Walk down event list
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
// Only remove events not marked kEvFNoDestory (engine events)
- if (!(eventi->code & kEvFNoDestory)) {
+ if (!(eventi->front().code & kEvFNoDestory)) {
// Handle queued music change events before deleting them
// This can happen in IHNM by music events set by sfQueueMusic()
// Fixes bug #2057987 - "IHNM: Music stops in Ellen's chapter"
- if (playQueuedMusic && ((eventi->code & EVENT_MASK) == kMusicEvent)) {
+ if (playQueuedMusic && ((eventi->front().code & EVENT_MASK) == kMusicEvent)) {
_vm->_music->stop();
- if (eventi->op == kEventPlay)
- _vm->_music->play(eventi->param, (MusicFlags)eventi->param2);
+ if (eventi->front().op == kEventPlay)
+ _vm->_music->play(eventi->front().param, (MusicFlags)eventi->front().param2);
}
- // Remove any events chained off this one
- for (chain_walk = eventi->chain; chain_walk != NULL; chain_walk = next_chain) {
- next_chain = chain_walk->chain;
- free(chain_walk);
- }
eventi = _eventList.reverse_erase(eventi);
}
}
-
- return SUCCESS;
}
// Removes all events from the list (even kEvFNoDestory)
-int Events::freeList() {
- Event *chain_walk;
- Event *next_chain;
-
- // Walk down event list
- EventList::iterator eventi = _eventList.begin();
- while (eventi != _eventList.end()) {
-
- // Remove any events chained off this one */
- for (chain_walk = eventi->chain; chain_walk != NULL; chain_walk = next_chain) {
- next_chain = chain_walk->chain;
- free(chain_walk);
- }
- eventi = _eventList.erase(eventi);
- }
-
- return SUCCESS;
+void Events::freeList() {
+ _eventList.clear();
}
// Walks down the event list, updating event times by 'msec'.
-int Events::processEventTime(long msec) {
+void Events::processEventTime(long msec) {
uint16 event_count = 0;
for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
- eventi->time -= msec;
+ eventi->front().time -= msec;
event_count++;
- if (eventi->type == kEvTImmediate)
+ if (eventi->front().type == kEvTImmediate)
break;
if (event_count > EVENT_WARNINGCOUNT) {
warning("Event list exceeds %u", EVENT_WARNINGCOUNT);
}
}
-
- return SUCCESS;
}
} // End of namespace Saga
diff --git a/engines/saga/events.h b/engines/saga/events.h
index d1530787c2..135c0beb55 100644
--- a/engines/saga/events.h
+++ b/engines/saga/events.h
@@ -142,13 +142,14 @@ struct Event {
long duration; // Duration of event
long d_reserved;
- Event *chain; // Event chain (For consecutive events)
Event() {
memset(this, 0, sizeof(*this));
}
};
-typedef Common::List<Event> EventList;
+typedef Common::List<Event> EventColumns;
+
+typedef Common::List<EventColumns> EventList;
#define EVENT_WARNINGCOUNT 1000
#define EVENT_MASK 0x00FF
@@ -164,19 +165,26 @@ class Events {
public:
Events(SagaEngine *vm);
~Events();
- int handleEvents(long msec);
- int clearList(bool playQueuedMusic = true);
- int freeList();
- Event *queue(Event *event);
- Event *chain(Event *headEvent, Event *addEvent);
+ void handleEvents(long msec);
+ void clearList(bool playQueuedMusic = true);
+ void freeList();
+
+ // Schedules an event in the event list; returns a pointer to the scheduled
+ // event columns suitable for chaining if desired.
+ EventColumns *queue(const Event &event) {
+ return chain(NULL, event);
+ }
+
+ // Places a 'event' on the end of an event columns given by 'eventColumns'
+ EventColumns *chain(EventColumns *eventColumns, const Event &event);
private:
int handleContinuous(Event *event);
int handleOneShot(Event *event);
int handleInterval(Event *event);
int handleImmediate(Event *event);
- int processEventTime(long msec);
- int initializeEvent(Event *event);
+ void processEventTime(long msec);
+ void initializeEvent(Event &event);
private:
SagaEngine *_vm;
diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index 5b7b7289eb..01e74d2984 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -41,11 +41,9 @@ Font::Font(SagaEngine *vm) : _vm(vm) {
assert(_vm->getFontsCount() > 0);
- _fonts = (FontData **)calloc(_vm->getFontsCount(), sizeof(*_fonts));
- _loadedFonts = 0;
-
+ _fonts.resize(_vm->getFontsCount());
for (i = 0; i < _vm->getFontsCount(); i++) {
- loadFont(_vm->getFontDescription(i)->fontResourceId);
+ loadFont(&_fonts[i], _vm->getFontDescription(i)->fontResourceId);
}
_fontMapping = 0;
@@ -53,25 +51,11 @@ Font::Font(SagaEngine *vm) : _vm(vm) {
Font::~Font() {
debug(8, "Font::~Font(): Freeing fonts.");
- int i;
-
- for (i = 0 ; i < _loadedFonts ; i++) {
- if (_fonts[i] != NULL) {
- free(_fonts[i]->normal.font);
- free(_fonts[i]->outline.font);
- }
-
- free(_fonts[i]);
- }
-
- free(_fonts);
}
-void Font::loadFont(uint32 fontResourceId) {
- FontData *font;
- byte *fontResourcePointer;
- size_t fontResourceLength;
+void Font::loadFont(FontData *font, uint32 fontResourceId) {
+ ByteArray fontResourceData;
int numBits;
int c;
ResourceContext *fontContext;
@@ -84,16 +68,13 @@ void Font::loadFont(uint32 fontResourceId) {
}
// Load font resource
- _vm->_resource->loadResource(fontContext, fontResourceId, fontResourcePointer, fontResourceLength);
+ _vm->_resource->loadResource(fontContext, fontResourceId, fontResourceData);
- if (fontResourceLength < FONT_DESCSIZE) {
- error("Font::loadFont() Invalid font length (%i < %i)", (int)fontResourceLength, FONT_DESCSIZE);
+ if (fontResourceData.size() < FONT_DESCSIZE) {
+ error("Font::loadFont() Invalid font length (%i < %i)", (int)fontResourceData.size(), FONT_DESCSIZE);
}
- MemoryReadStreamEndian readS(fontResourcePointer, fontResourceLength, fontContext->isBigEndian());
-
- // Create new font structure
- font = (FontData *)malloc(sizeof(*font));
+ ByteArrayReadStreamEndian readS(fontResourceData, fontContext->isBigEndian());
// Read font header
font->normal.header.charHeight = readS.readUint16();
@@ -123,20 +104,15 @@ 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);
- memcpy(font->normal.font, fontResourcePointer + FONT_DESCSIZE, fontResourceLength - FONT_DESCSIZE);
-
- free(fontResourcePointer);
+ font->normal.font.resize(fontResourceData.size() - FONT_DESCSIZE);
+ memcpy(font->normal.font.getBuffer(), fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
// Create outline font style
createOutline(font);
-
- // Set font data
- _fonts[_loadedFonts++] = font;
}
void Font::createOutline(FontData *font) {
@@ -145,12 +121,12 @@ void Font::createOutline(FontData *font) {
int newByteWidth;
int newRowLength = 0;
int currentByte;
- unsigned char *basePointer;
- unsigned char *srcPointer;
- unsigned char *destPointer1;
- unsigned char *destPointer2;
- unsigned char *destPointer3;
- unsigned char charRep;
+ byte *basePointer;
+ byte *srcPointer;
+ byte *destPointer1;
+ byte *destPointer2;
+ byte *destPointer3;
+ byte charRep;
// Populate new font style character data
for (i = 0; i < FONT_CHARCOUNT; i++) {
@@ -177,20 +153,20 @@ void Font::createOutline(FontData *font) {
font->outline.header.rowLength = newRowLength;
// Allocate new font representation storage
- font->outline.font = (unsigned char *)calloc(newRowLength, font->outline.header.charHeight);
+ font->outline.font.resize(newRowLength * font->outline.header.charHeight);
// Generate outline font representation
for (i = 0; i < FONT_CHARCOUNT; i++) {
for (row = 0; row < font->normal.header.charHeight; row++) {
for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
- basePointer = font->outline.font + font->outline.fontCharEntry[i].index + currentByte;
+ basePointer = &font->outline.font[font->outline.fontCharEntry[i].index + currentByte];
destPointer1 = basePointer + newRowLength * row;
destPointer2 = basePointer + newRowLength * (row + 1);
destPointer3 = basePointer + newRowLength * (row + 2);
if (currentByte > 0) {
// Get last two columns from previous byte
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
charRep = *srcPointer;
*destPointer1 |= ((charRep << 6) | (charRep << 7));
*destPointer2 |= ((charRep << 6) | (charRep << 7));
@@ -198,7 +174,7 @@ void Font::createOutline(FontData *font) {
}
if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
charRep = *srcPointer;
*destPointer1 |= charRep | (charRep >> 1) | (charRep >> 2);
*destPointer2 |= charRep | (charRep >> 1) | (charRep >> 2);
@@ -210,15 +186,15 @@ void Font::createOutline(FontData *font) {
// "Hollow out" character to prevent overdraw
for (row = 0; row < font->normal.header.charHeight; row++) {
for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
- destPointer2 = font->outline.font + font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte;
+ destPointer2 = &font->outline.font[font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte];
if (currentByte > 0) {
// Get last two columns from previous byte
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
*destPointer2 &= ((*srcPointer << 7) ^ 0xFFU);
}
if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
- srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
*destPointer2 &= ((*srcPointer >> 1) ^ 0xFFU);
}
}
@@ -289,7 +265,7 @@ void Font::draw(FontId fontId, const char *text, size_t count, const Common::Poi
void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags) {
const byte *textPointer;
- byte *c_dataPointer;
+ const byte *c_dataPointer;
int c_code;
int charRow = 0;
Point textPoint(point);
@@ -384,7 +360,7 @@ void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, co
break;
}
- c_dataPointer = drawFont.font + charRow * drawFont.header.rowLength + drawFont.fontCharEntry[c_code].index;
+ c_dataPointer = &drawFont.font[charRow * drawFont.header.rowLength + drawFont.fontCharEntry[c_code].index];
for (c_byte = 0; c_byte < c_byte_len; c_byte++, c_dataPointer++) {
// Check each bit, draw pixel if bit is set
@@ -610,7 +586,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..6f66545756 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -120,7 +120,7 @@ struct FontCharEntry {
struct FontStyle {
FontHeader header;
FontCharEntry fontCharEntry[256];
- byte *font;
+ ByteArray font;
};
struct FontData {
@@ -170,14 +170,14 @@ class Font {
void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
void textDraw(FontId fontId, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
- void loadFont(uint32 fontResourceId);
+ void loadFont(FontData *font, uint32 fontResourceId);
void createOutline(FontData *font);
void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
void outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
FontData *getFont(FontId fontId) {
validate(fontId);
- return _fonts[fontId];
+ return &_fonts[fontId];
}
int getHeight(FontId fontId) {
@@ -186,11 +186,11 @@ 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) {
- return (fontId < _loadedFonts);
+ return (uint(fontId) < _fonts.size());
}
int getByteLen(int numBits) const {
int byteLength = numBits / 8;
@@ -207,8 +207,7 @@ class Font {
int _fontMapping;
- int _loadedFonts;
- FontData **_fonts;
+ Common::Array<FontData> _fonts;
};
} // End of namespace Saga
diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp
index 40a633ac5d..77842acc7b 100644
--- a/engines/saga/gfx.cpp
+++ b/engines/saga/gfx.cpp
@@ -173,13 +173,11 @@ void Gfx::initPalette() {
error("Resource::loadGlobalResources() resource context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
- _vm->_resource->loadResource(resourceContext, RID_IHNM_DEFAULT_PALETTE,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_DEFAULT_PALETTE, resourceData);
- MemoryReadStream metaS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian metaS(resourceData);
for (int i = 0; i < 256; i++) {
_globalPalette[i].red = metaS.readByte();
@@ -187,8 +185,6 @@ void Gfx::initPalette() {
_globalPalette[i].blue = metaS.readByte();
}
- free(resourcePointer);
-
setPalette(_globalPalette, true);
}
@@ -504,22 +500,19 @@ void Gfx::setCursor(CursorType cursorType) {
break;
}
- byte *resource;
- size_t resourceLength;
- byte *image;
- size_t imageLength;
+ ByteArray resourceData;
+ ByteArray image;
int width, height;
if (resourceId != (uint32)-1) {
ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- _vm->_resource->loadResource(context, resourceId, resource, resourceLength);
+ _vm->_resource->loadResource(context, resourceId, resourceData);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &width, &height);
+ _vm->decodeBGImage(resourceData, image, &width, &height);
} else {
- resource = NULL;
width = height = 31;
- image = (byte *)calloc(width, height);
+ image.resize(width * height);
for (int i = 0; i < 14; i++) {
image[15 * 31 + i] = 1;
@@ -530,10 +523,7 @@ void Gfx::setCursor(CursorType cursorType) {
}
// Note: Hard-coded hotspot
- CursorMan.replaceCursor(image, width, height, 15, 15, 0);
-
- free(image);
- free(resource);
+ CursorMan.replaceCursor(image.getBuffer(), width, height, 15, 15, 0);
}
}
@@ -564,8 +554,9 @@ bool hitTestPoly(const Point *points, unsigned int npoints, const Point& test_po
// This method adds a dirty rectangle automatically
void Gfx::drawFrame(const Common::Point &p1, const Common::Point &p2, int color) {
- _backBuffer.drawFrame(p1, p2, color);
- _vm->_render->addDirtyRect(Common::Rect(p1.x, p1.y, p2.x + 1, p2.y + 1));
+ Common::Rect rect(MIN(p1.x, p2.x), MIN(p1.y, p2.y), MAX(p1.x, p2.x) + 1, MAX(p1.y, p2.y) + 1);
+ _backBuffer.frameRect(rect, color);
+ _vm->_render->addDirtyRect(rect);
}
// This method adds a dirty rectangle automatically
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
index f3ccad469f..18d88503ce 100644
--- a/engines/saga/gfx.h
+++ b/engines/saga/gfx.h
@@ -108,10 +108,7 @@ struct Surface : Graphics::Surface {
rect.right = w;
rect.bottom = h;
}
- void drawFrame(const Common::Point &p1, const Common::Point &p2, int color) {
- Common::Rect rect(MIN(p1.x, p2.x), MIN(p1.y, p2.y), MAX(p1.x, p2.x) + 1, MAX(p1.y, p2.y) + 1);
- frameRect(rect, color);
- }
+
void drawRect(const Common::Rect &destRect, int color) {
Common::Rect rect(w , h);
rect.clip(destRect);
@@ -198,7 +195,7 @@ public:
// WARNING: This method does not add a dirty rectangle automatically.
// Whenever it gets called, the corresponding caller must take care
// to add the corresponding dirty rectangle itself
- void drawPolyLine(Common::Point *points, int count, int color) {
+ void drawPolyLine(const Common::Point *points, int count, int color) {
_backBuffer.drawPolyLine(points, count, color);
}
diff --git a/engines/saga/image.cpp b/engines/saga/image.cpp
index 7d8eb83550..87d9e514c8 100644
--- a/engines/saga/image.cpp
+++ b/engines/saga/image.cpp
@@ -47,22 +47,18 @@ static int granulate(int value, int granularity) {
}
}
-int SagaEngine::decodeBGImage(const byte *image_data, size_t image_size,
- byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip) {
+bool SagaEngine::decodeBGImage(const ByteArray &imageData, ByteArray &outputBuffer, int *w, int *h, bool flip) {
ImageHeader hdr;
int modex_height;
const byte *RLE_data_ptr;
size_t RLE_data_len;
- byte *decode_buf;
- size_t decode_buf_len;
- byte *out_buf;
- size_t out_buf_len;
+ ByteArray decodeBuffer;
- if (image_size <= SAGA_IMAGE_DATA_OFFSET) {
- error("decodeBGImage() Image size is way too small (%d)", (int)image_size);
+ if (imageData.size() <= SAGA_IMAGE_DATA_OFFSET) {
+ error("decodeBGImage() Image size is way too small (%d)", (int)imageData.size());
}
- MemoryReadStreamEndian readS(image_data, image_size, isBigEndian());
+ ByteArrayReadStreamEndian readS(imageData, isBigEndian());
hdr.width = readS.readUint16();
hdr.height = readS.readUint16();
@@ -70,45 +66,36 @@ int SagaEngine::decodeBGImage(const byte *image_data, size_t image_size,
readS.readUint16();
readS.readUint16();
- RLE_data_ptr = image_data + SAGA_IMAGE_DATA_OFFSET;
- RLE_data_len = image_size - SAGA_IMAGE_DATA_OFFSET;
+ RLE_data_ptr = &imageData.front() + SAGA_IMAGE_DATA_OFFSET;
+ RLE_data_len = imageData.size() - SAGA_IMAGE_DATA_OFFSET;
modex_height = granulate(hdr.height, 4);
- decode_buf_len = hdr.width * modex_height;
- decode_buf = (byte *)malloc(decode_buf_len);
+ decodeBuffer.resize(hdr.width * modex_height);
- out_buf_len = hdr.width * hdr.height;
- out_buf = (byte *)malloc(out_buf_len);
+ outputBuffer.resize(hdr.width * hdr.height);
- if (decodeBGImageRLE(RLE_data_ptr,
- RLE_data_len, decode_buf, decode_buf_len) != SUCCESS) {
- free(decode_buf);
- free(out_buf);
- return FAILURE;
+ if (!decodeBGImageRLE(RLE_data_ptr, RLE_data_len, decodeBuffer)) {
+ return false;
}
- unbankBGImage(out_buf, decode_buf, hdr.width, hdr.height);
+ unbankBGImage(outputBuffer.getBuffer(), decodeBuffer.getBuffer(), hdr.width, hdr.height);
// For some reason bg images in IHNM are upside down
if (getGameId() == GID_IHNM && !flip) {
- flipImage(out_buf, hdr.width, hdr.height);
+ flipImage(outputBuffer.getBuffer(), hdr.width, hdr.height);
}
- free(decode_buf);
-
- *output_buf_len = out_buf_len;
- *output_buf = out_buf;
-
*w = hdr.width;
*h = hdr.height;
- return SUCCESS;
+ return true;
}
-int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len) {
+bool SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, ByteArray &outbuf) {
const byte *inbuf_ptr;
byte *outbuf_ptr;
+ byte *outbuf_start;
uint32 inbuf_remain;
const byte *inbuf_end;
@@ -134,18 +121,18 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
inbuf_ptr = inbuf;
inbuf_remain = inbuf_len;
- outbuf_ptr = outbuf;
- outbuf_remain = outbuf_len;
+ outbuf_start = outbuf_ptr = outbuf.getBuffer();
+ outbuf_remain = outbuf.size();
+ outbuf_end = (outbuf_start + outbuf_remain) - 1;
+ memset(outbuf_start, 0, outbuf_remain);
inbuf_end = (inbuf + inbuf_len) - 1;
- outbuf_end = (outbuf + outbuf_len) - 1;
- memset(outbuf, 0, outbuf_len);
while ((inbuf_remain > 1) && (outbuf_remain > 0) && !decode_err) {
if ((inbuf_ptr > inbuf_end) || (outbuf_ptr > outbuf_end)) {
- return FAILURE;
+ return false;
}
mark_byte = *inbuf_ptr++;
@@ -158,7 +145,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Uncompressed run follows: Max runlength 63
runcount = mark_byte & 0x3f;
if ((inbuf_remain < runcount) || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
for (c = 0; c < runcount; c++) {
@@ -173,7 +160,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Compressed run follows: Max runlength 63
runcount = (mark_byte & 0x3f) + 3;
if (!inbuf_remain || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
for (c = 0; c < runcount; c++) {
@@ -194,8 +181,8 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
runcount = ((mark_byte >> 3) & 0x07U) + 3;
backtrack_amount = *inbuf_ptr;
- if (!inbuf_remain || (backtrack_amount > (outbuf_ptr - outbuf)) || (runcount > outbuf_remain)) {
- return FAILURE;
+ if (!inbuf_remain || (backtrack_amount > (outbuf_ptr - outbuf_start)) || (runcount > outbuf_remain)) {
+ return false;
}
inbuf_ptr++;
@@ -224,7 +211,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
runcount = (mark_byte & 0x0F) + 1;
if ((inbuf_remain < (runcount + 2)) || (outbuf_remain < (runcount * 8))) {
- return FAILURE;
+ return false;
}
bitfield_byte1 = *inbuf_ptr++;
@@ -252,7 +239,7 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Uncompressed run follows
runcount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
if ((inbuf_remain < (runcount + 1)) || (outbuf_remain < runcount)) {
- return FAILURE;
+ return false;
}
inbuf_ptr++;
@@ -271,14 +258,14 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
// Repeat decoded sequence from output stream
backtrack_amount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
if (inbuf_remain < 2) {
- return FAILURE;
+ return false;
}
inbuf_ptr++;
runcount = *inbuf_ptr++;
- if ((backtrack_amount > (outbuf_ptr - outbuf)) || (outbuf_remain < runcount)) {
- return FAILURE;
+ if ((backtrack_amount > (outbuf_ptr - outbuf_start)) || (outbuf_remain < runcount)) {
+ return false;
}
backtrack_ptr = outbuf_ptr - backtrack_amount;
@@ -292,44 +279,42 @@ int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outb
continue;
break;
default:
- return FAILURE;
+ return false;
}
}
- return SUCCESS;
+ return true;
}
-int SagaEngine::flipImage(byte *img_buf, int columns, int scanlines) {
+void SagaEngine::flipImage(byte *imageBuffer, int columns, int scanlines) {
int line;
- byte *tmp_scan;
+ ByteArray tmp_scan;
byte *flip_p1;
byte *flip_p2;
+ byte *flip_tmp;
int flipcount = scanlines / 2;
- tmp_scan = (byte *)malloc(columns);
- if (tmp_scan == NULL) {
- return FAILURE;
+ tmp_scan.resize(columns);
+ flip_tmp = tmp_scan.getBuffer();
+ if (flip_tmp == NULL) {
+ return;
}
- flip_p1 = img_buf;
- flip_p2 = img_buf + (columns * (scanlines - 1));
+ flip_p1 = imageBuffer;
+ flip_p2 = imageBuffer + (columns * (scanlines - 1));
for (line = 0; line < flipcount; line++) {
- memcpy(tmp_scan, flip_p1, columns);
+ memcpy(flip_tmp, flip_p1, columns);
memcpy(flip_p1, flip_p2, columns);
- memcpy(flip_p2, tmp_scan, columns);
+ memcpy(flip_p2, flip_tmp, columns);
flip_p1 += columns;
flip_p2 -= columns;
}
-
- free(tmp_scan);
-
- return SUCCESS;
}
-int SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, int scanlines) {
+void SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, int scanlines) {
int x, y;
int temp;
int quadruple_rows;
@@ -424,15 +409,6 @@ int SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, i
default:
break;
}
- return SUCCESS;
-}
-
-const byte *SagaEngine::getImagePal(const byte *image_data, size_t image_size) {
- if (image_size <= SAGA_IMAGE_HEADER_LEN) {
- return NULL;
- }
-
- return image_data + SAGA_IMAGE_HEADER_LEN;
}
} // End of namespace Saga
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index c4b4688785..5b15fc9803 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -124,8 +124,7 @@ static const int IHNMTextStringIdsLUT[56] = {
#define buttonRes1 0x42544E01
Interface::Interface(SagaEngine *vm) : _vm(vm) {
- byte *resource;
- size_t resourceLength;
+ ByteArray resourceData;
int i;
#if 0
@@ -170,34 +169,27 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
}
}
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_mainPanel.image,
- &_mainPanel.imageLength, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
-
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _mainPanel.image, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
// Converse panel
_conversePanel.buttons = _vm->getDisplayInfo().conversePanelButtons;
_conversePanel.buttonsCount = _vm->getDisplayInfo().conversePanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_conversePanel.image,
- &_conversePanel.imageLength, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _conversePanel.image, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
// Option panel
if (!_vm->_script->isNonInteractiveDemo()) {
_optionPanel.buttons = _vm->getDisplayInfo().optionPanelButtons;
_optionPanel.buttonsCount = _vm->getDisplayInfo().optionPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_optionPanel.image,
- &_optionPanel.imageLength, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _optionPanel.image, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
} else {
_optionPanel.buttons = NULL;
_optionPanel.buttonsCount = 0;
- _optionPanel.sprites.spriteCount = 0;
+ _optionPanel.sprites.clear();
}
#ifdef ENABLE_IHNM
@@ -206,10 +198,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons;
_quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_quitPanel.image,
- &_quitPanel.imageLength, &_quitPanel.imageWidth, &_quitPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _quitPanel.image, &_quitPanel.imageWidth, &_quitPanel.imageHeight);
}
// Save panel
@@ -217,10 +207,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_savePanel.buttons = _vm->getDisplayInfo().savePanelButtons;
_savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_savePanel.image,
- &_savePanel.imageLength, &_savePanel.imageWidth, &_savePanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _savePanel.image, &_savePanel.imageWidth, &_savePanel.imageHeight);
}
// Load panel
@@ -228,10 +216,8 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons;
_loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount;
- _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resource, resourceLength);
- _vm->decodeBGImage(resource, resourceLength, &_loadPanel.image,
- &_loadPanel.imageLength, &_loadPanel.imageWidth, &_loadPanel.imageHeight);
- free(resource);
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->warningPanelResourceId, resourceData);
+ _vm->decodeBGImage(resourceData, _loadPanel.image, &_loadPanel.imageWidth, &_loadPanel.imageHeight);
}
#endif
@@ -323,16 +309,12 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
_inventoryStart = 0;
_inventoryEnd = 0;
_inventoryBox = 0;
- _inventorySize = ITE_INVENTORY_SIZE;
_saveReminderState = 0;
_optionSaveFileTop = 0;
_optionSaveFileTitleNumber = 0;
- _inventory = (uint16 *)calloc(_inventorySize, sizeof(uint16));
- if (_inventory == NULL) {
- error("Interface::Interface(): not enough memory");
- }
+ _inventory.resize(ITE_INVENTORY_SIZE);
_textInput = false;
_statusTextInput = false;
@@ -345,25 +327,6 @@ Interface::Interface(SagaEngine *vm) : _vm(vm) {
}
Interface::~Interface() {
- free(_inventory);
-
- free(_mainPanel.image);
- free(_conversePanel.image);
- free(_optionPanel.image);
- free(_quitPanel.image);
- free(_loadPanel.image);
- free(_savePanel.image);
-
- _mainPanel.sprites.freeMem();
- _conversePanel.sprites.freeMem();
- _optionPanel.sprites.freeMem();
- _quitPanel.sprites.freeMem();
- _loadPanel.sprites.freeMem();
- _savePanel.sprites.freeMem();
- _protectPanel.sprites.freeMem();
-
- _defPortraits.freeMem();
- _scenePortraits.freeMem();
}
void Interface::saveReminderCallback(void *refCon) {
@@ -768,7 +731,7 @@ void Interface::setStatusText(const char *text, int statusColor) {
}
void Interface::loadScenePortraits(int resourceId) {
- _scenePortraits.freeMem();
+ _scenePortraits.clear();
_vm->_sprite->loadList(resourceId, _scenePortraits);
}
@@ -817,7 +780,7 @@ void Interface::draw() {
if (_panelMode == kPanelMain || _panelMode == kPanelMap ||
(_panelMode == kPanelNull && _vm->isIHNMDemo())) {
_mainPanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _mainPanel.image);
+ _vm->_gfx->drawRegion(rect, _mainPanel.image.getBuffer());
for (int i = 0; i < kVerbTypeIdsMax; i++) {
if (_verbTypeToPanelButton[i] != NULL) {
@@ -826,7 +789,7 @@ void Interface::draw() {
}
} else if (_panelMode == kPanelConverse) {
_conversePanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _conversePanel.image);
+ _vm->_gfx->drawRegion(rect, _conversePanel.image.getBuffer());
converseDisplayTextLines();
}
@@ -847,7 +810,7 @@ void Interface::draw() {
// can tell this is what the original engine does. And it keeps
// ITE from crashing when entering the Elk King's court.
- if (_rightPortrait >= _scenePortraits.spriteCount)
+ if (_rightPortrait >= (int)_scenePortraits.size())
_rightPortrait = 0;
_vm->_sprite->draw(_scenePortraits, _rightPortrait, rightPortraitPoint, 256);
@@ -960,7 +923,7 @@ void Interface::drawOption() {
int spritenum = 0;
_optionPanel.getRect(rect);
- _vm->_gfx->drawRegion(rect, _optionPanel.image);
+ _vm->_gfx->drawRegion(rect, _optionPanel.image.getBuffer());
for (int i = 0; i < _optionPanel.buttonsCount; i++) {
panelButton = &_optionPanel.buttons[i];
@@ -1037,7 +1000,7 @@ void Interface::drawQuit() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _quitPanel.image);
+ _vm->_gfx->drawRegion(rect, _quitPanel.image.getBuffer());
for (i = 0; i < _quitPanel.buttonsCount; i++) {
panelButton = &_quitPanel.buttons[i];
@@ -1103,7 +1066,7 @@ void Interface::drawLoad() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _loadPanel.image);
+ _vm->_gfx->drawRegion(rect, _loadPanel.image.getBuffer());
for (i = 0; i < _loadPanel.buttonsCount; i++) {
panelButton = &_loadPanel.buttons[i];
@@ -1323,7 +1286,7 @@ void Interface::drawSave() {
if (_vm->getGameId() == GID_ITE)
drawButtonBox(rect, kButton, false);
else
- _vm->_gfx->drawRegion(rect, _savePanel.image);
+ _vm->_gfx->drawRegion(rect, _savePanel.image.getBuffer());
for (i = 0; i < _savePanel.buttonsCount; i++) {
panelButton = &_savePanel.buttons[i];
@@ -1398,7 +1361,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)) {
@@ -1587,7 +1550,7 @@ void Interface::handleChapterSelectionClick(const Point& mousePoint) {
event.param4 = obj; // Object
event.param5 = 0; // With Object
event.param6 = obj; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
}
@@ -2064,7 +2027,7 @@ void Interface::updateInventory(int pos) {
}
void Interface::addToInventory(int objectId) {
- if (_inventoryCount >= _inventorySize) {
+ if (uint(_inventoryCount) >= _inventory.size()) {
return;
}
@@ -2166,43 +2129,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;
@@ -2281,14 +2244,22 @@ void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelBut
}
break;
case kTextMusic:
- if (_vm->_musicVolume)
+ if (_vm->_musicVolume) {
textId = kText10Percent + _vm->_musicVolume / 25 - 1;
+ if (textId > kTextMax) {
+ textId = kTextMax;
+ }
+ }
else
textId = kTextOff;
break;
case kTextSound:
- if (_vm->_soundVolume)
+ if (_vm->_soundVolume) {
textId = kText10Percent + _vm->_soundVolume / 25 - 1;
+ if (textId > kTextMax) {
+ textId = kTextMax;
+ }
+ }
else
textId = kTextOff;
break;
@@ -2423,18 +2394,9 @@ void Interface::drawVerbPanelText(PanelButton *panelButton, KnownColor textKnown
// Converse stuff
-void Interface::converseInit() {
- for (int i = 0; i < CONVERSE_MAX_TEXTS; i++)
- _converseText[i].text = NULL;
- converseClear();
-}
-
void Interface::converseClear() {
for (int i = 0; i < CONVERSE_MAX_TEXTS; i++) {
- if (_converseText[i].text != NULL) {
- free(_converseText[i].text);
- _converseText[i].text = NULL;
- }
+ _converseText[i].text.clear();
_converseText[i].stringNum = -1;
_converseText[i].replyId = 0;
_converseText[i].replyFlags = 0;
@@ -2480,8 +2442,8 @@ bool Interface::converseAddText(const char *text, int strId, int replyId, byte r
return true;
}
- _converseText[_converseTextCount].text = (char *)malloc(i + 1);
- strncpy(_converseText[_converseTextCount].text, _converseWorkString, i);
+ _converseText[_converseTextCount].text.resize(i + 1);
+ strncpy(&_converseText[_converseTextCount].text.front(), _converseWorkString, i);
_converseText[_converseTextCount].strId = strId;
_converseText[_converseTextCount].text[i] = 0;
@@ -2591,7 +2553,7 @@ void Interface::converseDisplayTextLines() {
rect.left += 8;
_vm->_gfx->drawRect(rect, backgnd);
- str = _converseText[relPos].text;
+ str = &_converseText[relPos].text.front();
if (_converseText[relPos].textNum == 0) { // first entry
textPoint.x = rect.left - 6;
@@ -2725,10 +2687,9 @@ void Interface::loadState(Common::InSaveFile *in) {
void Interface::mapPanelShow() {
int i;
- byte *resource;
- size_t resourceLength, imageLength;
+ ByteArray resourceData;
Rect rect;
- byte *image;
+ ByteArray image;
int imageWidth, imageHeight;
const byte *pal;
PalEntry cPal[PAL_ENTRIES];
@@ -2737,9 +2698,8 @@ void Interface::mapPanelShow() {
rect.left = rect.top = 0;
- _vm->_resource->loadResource(_interfaceContext,
- _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resource, resourceLength);
- if (resourceLength == 0) {
+ _vm->_resource->loadResource(_interfaceContext, _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resourceData);
+ if (resourceData.empty()) {
error("Interface::mapPanelShow() unable to load Tycho map resource");
}
@@ -2753,8 +2713,8 @@ void Interface::mapPanelShow() {
_vm->_render->setFlag(RF_MAP);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
- pal = _vm->getImagePal(resource, resourceLength);
+ _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
+ pal = _vm->getImagePal(resourceData);
for (i = 0; i < PAL_ENTRIES; i++) {
cPal[i].red = *pal++;
@@ -2765,7 +2725,7 @@ void Interface::mapPanelShow() {
rect.setWidth(imageWidth);
rect.setHeight(imageHeight);
- _vm->_gfx->drawRegion(rect, image);
+ _vm->_gfx->drawRegion(rect, image.getBuffer());
// Evil Evil
for (i = 0; i < 6 ; i++) {
@@ -2774,8 +2734,6 @@ void Interface::mapPanelShow() {
_vm->_system->delayMillis(5);
}
- free(resource);
- free(image);
setSaveReminderState(false);
@@ -2832,10 +2790,9 @@ void Interface::keyBoss() {
_vm->_music->pause();
int i;
- byte *resource;
- size_t resourceLength, imageLength;
+ ByteArray resourceData;
Rect rect;
- byte *image;
+ ByteArray image;
int imageWidth, imageHeight;
const byte *pal;
PalEntry cPal[PAL_ENTRIES];
@@ -2844,20 +2801,20 @@ void Interface::keyBoss() {
rect.left = rect.top = 0;
- _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resource, resourceLength);
- if (resourceLength == 0) {
+ _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resourceData);
+ if (resourceData.empty()) {
error("Interface::bossKey() unable to load Boss image resource");
}
_bossMode = _panelMode;
setMode(kPanelBoss);
- _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
+ _vm->decodeBGImage(resourceData, image, &imageWidth, &imageHeight);
rect.setWidth(imageWidth);
rect.setHeight(imageHeight);
_vm->_gfx->getCurrentPal(_mapSavedPal);
- pal = _vm->getImagePal(resource, resourceLength);
+ pal = _vm->getImagePal(resourceData);
cPal[0].red = 0;
cPal[0].green = 0;
@@ -2869,12 +2826,9 @@ void Interface::keyBoss() {
cPal[i].blue = 128;
}
- _vm->_gfx->drawRegion(rect, image);
+ _vm->_gfx->drawRegion(rect, image.getBuffer());
_vm->_gfx->setPalette(cPal);
-
- free(resource);
- free(image);
}
diff --git a/engines/saga/interface.h b/engines/saga/interface.h
index 0fbe5bef20..b9a96653a7 100644
--- a/engines/saga/interface.h
+++ b/engines/saga/interface.h
@@ -94,8 +94,7 @@ enum FadeModes {
struct InterfacePanel {
int x;
int y;
- byte *image;
- size_t imageLength;
+ ByteArray image;
int imageWidth;
int imageHeight;
@@ -106,8 +105,6 @@ struct InterfacePanel {
InterfacePanel() {
x = y = 0;
- image = NULL;
- imageLength = 0;
imageWidth = imageHeight = 0;
currentButton = NULL;
buttonsCount = 0;
@@ -164,7 +161,7 @@ struct InterfacePanel {
};
struct Converse {
- char *text;
+ Common::Array<char> text;
int strId;
int stringNum;
int textNum;
@@ -356,7 +353,6 @@ private:
void processStatusTextInput(Common::KeyState keystate);
public:
- void converseInit();
void converseClear();
bool converseAddText(const char *text, int strId, int replyId, byte replyFlags, int replyBit);
void converseDisplayText();
@@ -431,8 +427,7 @@ private:
Point _lastMousePoint;
- uint16 *_inventory;
- int _inventorySize;
+ Common::Array<uint16> _inventory;
int _inventoryStart;
int _inventoryEnd;
int _inventoryPos;
diff --git a/engines/saga/introproc_ihnm.cpp b/engines/saga/introproc_ihnm.cpp
index e149753dfd..2053c7158f 100644
--- a/engines/saga/introproc_ihnm.cpp
+++ b/engines/saga/introproc_ihnm.cpp
@@ -83,19 +83,18 @@ int Scene::IHNMStartProc() {
}
_vm->_music->setVolume(0, 1000);
- _vm->_anim->freeCutawayList();
+ _vm->_anim->clearCutawayList();
// Queue first scene
firstScene.loadFlag = kLoadBySceneNumber;
firstScene.sceneDescriptor = -1;
- firstScene.sceneDescription = NULL;
firstScene.sceneSkipTarget = false;
firstScene.sceneProc = NULL;
firstScene.transitionType = kTransitionFade;
firstScene.actorsEntrance = 0;
firstScene.chapter = -1;
- _vm->_scene->queueScene(&firstScene);
+ _vm->_scene->queueScene(firstScene);
return SUCCESS;
}
@@ -114,7 +113,7 @@ int Scene::IHNMCreditsProc() {
}
_vm->_music->setVolume(0, 1000);
- _vm->_anim->freeCutawayList();
+ _vm->_anim->clearCutawayList();
return SUCCESS;
}
@@ -122,8 +121,7 @@ int Scene::IHNMCreditsProc() {
void Scene::IHNMLoadCutaways() {
ResourceContext *resourceContext;
//ResourceContext *soundContext;
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
if (resourceContext == NULL) {
@@ -131,18 +129,16 @@ void Scene::IHNMLoadCutaways() {
}
if (!_vm->isIHNMDemo())
- _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_INTRO_CUTAWAYS, resourceData);
else
- _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_INTRO_CUTAWAYS, resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Scene::IHNMStartProc() Can't load cutaway list");
}
// Load the cutaways for the title screens
- _vm->_anim->loadCutawayList(resourcePointer, resourceLength);
-
- free(resourcePointer);
+ _vm->_anim->loadCutawayList(resourceData);
}
bool Scene::checkKey() {
diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp
index 83fdadf59e..ae7dedefa5 100644
--- a/engines/saga/introproc_ite.cpp
+++ b/engines/saga/introproc_ite.cpp
@@ -61,15 +61,15 @@ using Common::IT_ITA;
#define MUSIC_2 10
LoadSceneParams ITE_IntroList[] = {
- {RID_ITE_INTRO_ANIM_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroAnimProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_1, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave1Proc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_2, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave2Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_3, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave3Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_CAVE_SCENE_4, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave4Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_VALLEY_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroValleyProc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_TREEHOUSE_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroTreeHouseProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_FAIREPATH_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFairePathProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
- {RID_ITE_FAIRETENT_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFaireTentProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}
+ {RID_ITE_INTRO_ANIM_SCENE, kLoadByResourceId, Scene::SC_ITEIntroAnimProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_1, kLoadByResourceId, Scene::SC_ITEIntroCave1Proc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_2, kLoadByResourceId, Scene::SC_ITEIntroCave2Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_3, kLoadByResourceId, Scene::SC_ITEIntroCave3Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_4, kLoadByResourceId, Scene::SC_ITEIntroCave4Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_VALLEY_SCENE, kLoadByResourceId, Scene::SC_ITEIntroValleyProc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_TREEHOUSE_SCENE, kLoadByResourceId, Scene::SC_ITEIntroTreeHouseProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIREPATH_SCENE, kLoadByResourceId, Scene::SC_ITEIntroFairePathProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIRETENT_SCENE, kLoadByResourceId, Scene::SC_ITEIntroFaireTentProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}
};
int Scene::ITEStartProc() {
@@ -84,25 +84,24 @@ int Scene::ITEStartProc() {
for (i = 0; i < scenesCount; i++) {
tempScene = ITE_IntroList[i];
tempScene.sceneDescriptor = _vm->_resource->convertResourceId(tempScene.sceneDescriptor);
- _vm->_scene->queueScene(&tempScene);
+ _vm->_scene->queueScene(tempScene);
}
firstScene.loadFlag = kLoadBySceneNumber;
firstScene.sceneDescriptor = _vm->getStartSceneNumber();
- firstScene.sceneDescription = NULL;
firstScene.sceneSkipTarget = true;
firstScene.sceneProc = NULL;
firstScene.transitionType = kTransitionFade;
firstScene.actorsEntrance = 0;
firstScene.chapter = -1;
- _vm->_scene->queueScene(&firstScene);
+ _vm->_scene->queueScene(firstScene);
return SUCCESS;
}
-Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]) {
+EventColumns *Scene::ITEQueueDialogue(EventColumns *eventColumns, int n_dialogues, const IntroDialogue dialogue[]) {
TextListEntry textEntry;
TextListEntry *entry;
Event event;
@@ -136,7 +135,7 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventDisplay;
event.data = entry;
event.time = (i == 0) ? 0 : VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
// Play voice
event.type = kEvTOneshot;
@@ -144,7 +143,7 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventPlay;
event.param = dialogue[i].i_voice_rn;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
voice_len = _vm->_sndRes->getVoiceLength(dialogue[i].i_voice_rn);
if (voice_len < 0) {
@@ -157,10 +156,10 @@ Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialo
event.op = kEventRemove;
event.data = entry;
event.time = voice_len;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
- return q_event;
+ return eventColumns;
}
enum {
@@ -180,7 +179,7 @@ enum {
// Queue a page of credits text. The original interpreter did word-wrapping
// automatically. We currently don't.
-Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) {
+EventColumns *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) {
int game;
Common::Language lang;
@@ -241,7 +240,7 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
TextListEntry textEntry;
TextListEntry *entry;
Event event;
- Event *q_event = NULL;
+ EventColumns *eventColumns = NULL;
textEntry.knownColor = kKnownColorSubtitleTextColor;
textEntry.effectKnownColor = kKnownColorTransparent;
@@ -283,7 +282,7 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
event.op = kEventDisplay;
event.data = entry;
event.time = delta_time;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Remove text
event.type = kEvTOneshot;
@@ -291,12 +290,12 @@ Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const
event.op = kEventRemove;
event.data = entry;
event.time = duration;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
y += (_vm->_font->getHeight(font) + line_spacing);
}
- return q_event;
+ return eventColumns;
}
int Scene::SC_ITEIntroAnimProc(int param, void *refCon) {
@@ -306,7 +305,7 @@ int Scene::SC_ITEIntroAnimProc(int param, void *refCon) {
// Handles the introductory Dreamer's Guild / NWC logo animation scene.
int Scene::ITEIntroAnimProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
switch (param) {
case SCENE_BEGIN:{
@@ -317,7 +316,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.op = kEventDisplay;
event.param = kEvPSetPalette;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
debug(3, "Intro animation procedure started.");
debug(3, "Linking animation resources...");
@@ -355,7 +354,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue intro music playback
event.type = kEvTOneshot;
@@ -364,7 +363,7 @@ int Scene::ITEIntroAnimProc(int param) {
event.param2 = MUSIC_LOOP;
event.op = kEventPlay;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
break;
case SCENE_END:
@@ -384,7 +383,7 @@ int Scene::SC_ITEIntroCave1Proc(int param, void *refCon) {
// Handles first introductory cave painting scene
int Scene::ITEIntroCave1Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -468,24 +467,24 @@ int Scene::ITEIntroCave1Proc(int param) {
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -499,7 +498,7 @@ int Scene::SC_ITEIntroCave2Proc(int param, void *refCon) {
// Handles second introductory cave painting scene
int Scene::ITEIntroCave2Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -566,30 +565,30 @@ int Scene::ITEIntroCave2Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -603,7 +602,7 @@ int Scene::SC_ITEIntroCave3Proc(int param, void *refCon) {
// Handles third introductory cave painting scene
int Scene::ITEIntroCave3Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -671,30 +670,30 @@ int Scene::ITEIntroCave3Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -708,7 +707,7 @@ int Scene::SC_ITEIntroCave4Proc(int param, void *refCon) {
// Handles fourth introductory cave painting scene
int Scene::ITEIntroCave4Proc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
int lang = 0;
if (_vm->getLanguage() == Common::DE_DEU)
@@ -789,30 +788,30 @@ int Scene::ITEIntroCave4Proc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin palette cycling animation for candles
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue narrator dialogue list
- q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+ ITEQueueDialogue(eventColumns, n_dialogues, dialogue[lang]);
// End scene after last dialogue over
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = VOICE_PAD;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
break;
default:
- warning("Illegal scene procedure paramater");
+ warning("Illegal scene procedure parameter");
break;
}
@@ -826,7 +825,7 @@ int Scene::SC_ITEIntroValleyProc(int param, void *refCon) {
// Handles intro title scene (valley overlook)
int Scene::ITEIntroValleyProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits[] = {
{EN_ANY, kITEAny, kCHeader, "Producer"},
@@ -856,7 +855,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin ITE title theme music
_vm->_music->stop();
@@ -867,7 +866,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.param2 = MUSIC_NORMAL;
event.op = kEventPlay;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Pause animation before logo
event.type = kEvTOneshot;
@@ -875,7 +874,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventStop;
event.param = 0;
event.time = 3000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Display logo
event.type = kEvTContinuous;
@@ -883,7 +882,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventDissolveBGMask;
event.time = 0;
event.duration = LOGO_DISSOLVE_DURATION;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Remove logo
event.type = kEvTContinuous;
@@ -891,7 +890,7 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventDissolve;
event.time = 3000;
event.duration = LOGO_DISSOLVE_DURATION;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Unpause animation before logo
event.type = kEvTOneshot;
@@ -899,17 +898,17 @@ int Scene::ITEIntroValleyProc(int param) {
event.op = kEventPlay;
event.time = 0;
event.param = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue game credits list
- q_event = ITEQueueCredits(9000, CREDIT_DURATION1, n_credits, credits);
+ eventColumns = ITEQueueCredits(9000, CREDIT_DURATION1, n_credits, credits);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -929,7 +928,7 @@ int Scene::SC_ITEIntroTreeHouseProc(int param, void *refCon) {
// Handles second intro credit screen (treehouse view)
int Scene::ITEIntroTreeHouseProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits1[] = {
{EN_ANY, kITEAny, kCHeader, "Game Design"},
@@ -981,7 +980,7 @@ int Scene::ITEIntroTreeHouseProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
if (_vm->_anim->hasAnimation(0)) {
// Begin title screen background animation
@@ -992,19 +991,19 @@ int Scene::ITEIntroTreeHouseProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
// Queue game credits list
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -1024,7 +1023,7 @@ int Scene::SC_ITEIntroFairePathProc(int param, void *refCon) {
// Handles third intro credit screen (path to puzzle tent)
int Scene::ITEIntroFairePathProc(int param) {
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static const IntroCredit credits1[] = {
{EN_ANY, kITEAny, kCHeader, "Programming"},
@@ -1063,7 +1062,7 @@ int Scene::ITEIntroFairePathProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// Begin title screen background animation
_vm->_anim->setCycles(0, -1);
@@ -1073,18 +1072,18 @@ int Scene::ITEIntroFairePathProc(int param) {
event.op = kEventPlay;
event.param = 0;
event.time = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Queue game credits list
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
- q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ eventColumns = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
// End scene after credit display
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 1000;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
@@ -1104,8 +1103,7 @@ int Scene::SC_ITEIntroFaireTentProc(int param, void *refCon) {
// Handles fourth intro credit screen (treehouse view)
int Scene::ITEIntroFaireTentProc(int param) {
Event event;
- Event *q_event;
- Event *q_event_start;
+ EventColumns *eventColumns;
switch (param) {
case SCENE_BEGIN:
@@ -1116,14 +1114,14 @@ int Scene::ITEIntroFaireTentProc(int param) {
event.op = kEventDissolve;
event.time = 0;
event.duration = DISSOLVE_DURATION;
- q_event_start = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// End scene after momentary pause
event.type = kEvTOneshot;
event.code = kSceneEvent;
event.op = kEventEnd;
event.time = 5000;
- q_event = _vm->_events->chain(q_event_start, &event);
+ _vm->_events->chain(eventColumns, event);
break;
case SCENE_END:
diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp
index 8999211f2a..6450af268a 100644
--- a/engines/saga/isomap.cpp
+++ b/engines/saga/isomap.cpp
@@ -85,34 +85,40 @@ static const IsoMap::TilePoint hardDirTable[8] = {
{ 0, 1, 0, SAGA_STRAIGHT_HARD_COST},
};
-IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
- _tileData = NULL;
- _tileDataLength = 0;
+static const int16 directions[8][2] = {
+ { 16, 16},
+ { 16, 0},
+ { 16, -16},
+ { 0, -16},
+ { -16, -16},
+ { -16, 0},
+ { -16, 16},
+ { 0, 16}
+};
- _multiTableData = NULL;
- _multiDataCount = 0;
+IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
_viewScroll.x = (128 - 8) * 16;
_viewScroll.x = (128 - 8) * 16 - 64;
_viewDiff = 1;
-
}
-void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
+void IsoMap::loadImages(const ByteArray &resourceData) {
IsoTileData *tileData;
uint16 i;
size_t offsetDiff;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadImages wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
readS.readUint16(); // skip
i = readS.readUint16();
i = i / SAGA_ISOTILEDATA_LEN;
_tilesTable.resize(i);
-
+ Common::Array<size_t> tempOffsets;
+ tempOffsets.resize(_tilesTable.size());
readS.seek(0);
@@ -120,7 +126,7 @@ void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
tileData = &_tilesTable[i];
tileData->height = readS.readByte();
tileData->attributes = readS.readSByte();
- tileData->offset = readS.readUint16();
+ tempOffsets[i] = readS.readUint16();
tileData->terrainMask = readS.readUint16();
tileData->FGDBGDAttr = readS.readByte();
readS.readByte(); //skip
@@ -128,26 +134,25 @@ void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
offsetDiff = readS.pos();
- _tileDataLength = resourceLength - offsetDiff;
- _tileData = (byte*)malloc(_tileDataLength);
- memcpy(_tileData, resourcePointer + offsetDiff, _tileDataLength);
+ _tileData.resize(resourceData.size() - offsetDiff);
+ memcpy(_tileData.getBuffer(), resourceData.getBuffer() + offsetDiff, _tileData.size());
for (i = 0; i < _tilesTable.size(); i++) {
- _tilesTable[i].offset -= offsetDiff;
+ _tilesTable[i].tilePointer = _tileData.getBuffer() + tempOffsets[i] - offsetDiff;
}
}
-void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadPlatforms(const ByteArray &resourceData) {
TilePlatformData *tilePlatformData;
uint16 i, x, y;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadPlatforms wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
- i = resourceLength / SAGA_TILEPLATFORMDATA_LEN;
+ i = resourceData.size() / SAGA_TILEPLATFORMDATA_LEN;
_tilePlatformList.resize(i);
for (i = 0; i < _tilePlatformList.size(); i++) {
@@ -166,14 +171,14 @@ void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength)
}
-void IsoMap::loadMap(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMap(const ByteArray &resourceData) {
uint16 x, y;
- if (resourceLength != SAGA_TILEMAP_LEN) {
- error("IsoMap::loadMap wrong resourceLength");
+ if (resourceData.size() != SAGA_TILEMAP_LEN) {
+ error("IsoMap::loadMap wrong resource length %d", resourceData.size());
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
_tileMap.edgeType = readS.readByte();
readS.readByte(); //skip
@@ -185,16 +190,16 @@ void IsoMap::loadMap(const byte * resourcePointer, size_t resourceLength) {
}
-void IsoMap::loadMetaTiles(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMetaTiles(const ByteArray &resourceData) {
MetaTileData *metaTileData;
uint16 i, j;
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
- i = resourceLength / SAGA_METATILEDATA_LEN;
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
+ i = resourceData.size() / SAGA_METATILEDATA_LEN;
_metaTileList.resize(i);
for (i = 0; i < _metaTileList.size(); i++) {
@@ -207,16 +212,16 @@ void IsoMap::loadMetaTiles(const byte * resourcePointer, size_t resourceLength)
}
}
-void IsoMap::loadMulti(const byte * resourcePointer, size_t resourceLength) {
+void IsoMap::loadMulti(const ByteArray &resourceData) {
MultiTileEntryData *multiTileEntryData;
uint16 i;
int16 offsetDiff;
- if (resourceLength < 2) {
+ if (resourceData.size() < 2) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
i = readS.readUint16();
_multiTable.resize(i);
@@ -240,26 +245,21 @@ void IsoMap::loadMulti(const byte * resourcePointer, size_t resourceLength) {
_multiTable[i].offset -= offsetDiff;
}
- _multiDataCount = (readS.size() - readS.pos()) / 2;
+ uint16 multiDataCount = (readS.size() - readS.pos()) / 2;
- _multiTableData = (int16 *)malloc(_multiDataCount * sizeof(*_multiTableData));
- for (i = 0; i < _multiDataCount; i++) {
+ _multiTableData.resize(multiDataCount);
+ for (i = 0; i < _multiTableData.size(); i++) {
_multiTableData[i] = readS.readSint16();
}
}
-void IsoMap::freeMem() {
+void IsoMap::clear() {
_tilesTable.clear();
_tilePlatformList.clear();
_metaTileList.clear();
_multiTable.clear();
-
- free(_tileData);
- _tileData = NULL;
-
- free(_multiTableData);
- _multiTableData = NULL;
- _multiDataCount = 0;
+ _tileData.clear();
+ _multiTableData.clear();
}
void IsoMap::adjustScroll(bool jump) {
@@ -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) {
@@ -344,12 +344,12 @@ int16 IsoMap::findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH) {
state = multiTileEntryData->currentState;
offset = (ru + state * multiTileEntryData->uSize) * multiTileEntryData->vSize + rv;
- offset *= sizeof(*_multiTableData);
+ offset *= sizeof(int16);
offset += multiTileEntryData->offset;
- if (offset + sizeof(*_multiTableData) - 1 >= _multiDataCount * sizeof(*_multiTableData)) {
+ if (offset + sizeof(int16) > _multiTableData.size() * sizeof(int16)) {
error("wrong multiTileEntryData->offset");
}
- tiles = (int16*)((byte*)_multiTableData + offset);
+ tiles = (int16*)((byte*)&_multiTableData.front() + offset);
tileIndex = *tiles;
if (tileIndex >= 256) {
warning("something terrible happened");
@@ -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];
@@ -707,7 +707,7 @@ void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *loca
return;
}
- tilePointer = _tileData + _tilesTable[tileIndex].offset;
+ tilePointer = _tilesTable[tileIndex].tilePointer;
height = _tilesTable[tileIndex].height;
if ((height <= 8) || (height > 64)) {
@@ -831,18 +831,30 @@ void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *loca
widthCount += fgRunCount;
count = 0;
- while ((col < _tileClip.left) && (count < fgRunCount)) {
- count++;
- col++;
+ int colDiff = _tileClip.left - col;
+ if (colDiff > 0) {
+ if (colDiff > fgRunCount) {
+ colDiff = fgRunCount;
+ }
+ count = colDiff;
+ col += colDiff;
}
- while ((col < _tileClip.right) && (count < fgRunCount)) {
- assert(_vm->_gfx->getBackBufferPixels() <= (byte *)(drawPointer + count));
- assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width *
- _vm->getDisplayInfo().height)) > (byte *)(drawPointer + count));
- drawPointer[count] = readPointer[count];
- count++;
- col++;
+
+ colDiff = _tileClip.right - col;
+ if (colDiff > 0) {
+ int countDiff = fgRunCount - count;
+ if (colDiff > countDiff) {
+ colDiff = countDiff;
+ }
+ if (colDiff > 0) {
+ byte *dst = (byte *)(drawPointer + count);
+ assert(_vm->_gfx->getBackBufferPixels() <= dst);
+ assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >= (byte *)(dst + colDiff));
+ memcpy(dst, (readPointer + count), colDiff);
+ col += colDiff;
+ }
}
+
readPointer += fgRunCount;
drawPointer += fgRunCount;
}
@@ -955,7 +967,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++;
@@ -1149,8 +1161,6 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista
int16 vBase;
int16 u;
int16 v;
- int i;
- ActorData *actor;
TilePoint tilePoint;
uint16 dir;
int16 dist;
@@ -1171,8 +1181,7 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista
memset( &_searchArray, 0, sizeof(_searchArray));
- for (i = 0; i < _vm->_actor->_actorsCount; i++) {
- actor = _vm->_actor->_actors[i];
+ for (ActorDataArray::const_iterator actor = _vm->_actor->_actors.begin(); actor != _vm->_actor->_actors.end(); ++actor) {
if (!actor->_inScene) continue;
u = (actor->_location.u() >> 4) - uBase;
@@ -1211,7 +1220,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 +1361,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 {
@@ -1452,14 +1461,13 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo
actor->_walkStepsCount = i;
if (i) {
- actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i );
+ actor->_tileDirections.resize(i);
+ memcpy(&actor->_tileDirections.front(), res, i);
}
}
void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
- ActorData *other;
int i;
int16 u;
int16 v;
@@ -1499,10 +1507,9 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio
if (!(actor->_actorFlags & kActorNoCollide) &&
(_vm->_scene->currentSceneResourceId() != ITE_SCENE_OVERMAP)) {
- for (i = 0; i < _vm->_actor->_actorsCount; i++) {
- other = _vm->_actor->_actors[i];
+ for (ActorDataArray::const_iterator other = _vm->_actor->_actors.begin(); other != _vm->_actor->_actors.end(); ++other) {
if (!other->_inScene) continue;
- if (other == actor) continue;
+ if (other->_id == actor->_id) continue;
u = (other->_location.u() >> 4) - uBase;
v = (other->_location.v() >> 4) - vBase;
@@ -1585,8 +1592,8 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio
actor->_walkStepsCount = i;
if (i) {
- actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i );
+ actor->_tileDirections.resize(i);
+ memcpy(&actor->_tileDirections.front(), res, i);
}
}
@@ -1601,19 +1608,6 @@ void IsoMap::setTileDoorState(int doorNumber, int doorState) {
multiTileEntryData->currentState = doorState;
}
-static const int16 directions[8][2] = {
- { 16, 16},
- { 16, 0},
- { 16, -16},
- { 0, -16},
- { -16, -16},
- { -16, 0},
- { -16, 16},
- { 0, 16}
-};
-
-
-
bool IsoMap::nextTileTarget(ActorData* actor) {
uint16 dir;
diff --git a/engines/saga/isomap.h b/engines/saga/isomap.h
index 46173e2b13..7dc7dff8cd 100644
--- a/engines/saga/isomap.h
+++ b/engines/saga/isomap.h
@@ -95,7 +95,7 @@ enum TileMapEdgeType {
struct IsoTileData {
byte height;
int8 attributes;
- size_t offset;
+ byte *tilePointer;
uint16 terrainMask;
byte FGDBGDAttr;
int8 GetMaskRule() const {
@@ -154,14 +154,13 @@ class IsoMap {
public:
IsoMap(SagaEngine *vm);
~IsoMap() {
- freeMem();
}
- void loadImages(const byte * resourcePointer, size_t resourceLength);
- void loadMap(const byte * resourcePointer, size_t resourceLength);
- void loadPlatforms(const byte * resourcePointer, size_t resourceLength);
- void loadMetaTiles(const byte * resourcePointer, size_t resourceLength);
- void loadMulti(const byte * resourcePointer, size_t resourceLength);
- void freeMem();
+ void loadImages(const ByteArray &resourceData);
+ void loadMap(const ByteArray &resourceData);
+ void loadPlatforms(const ByteArray &resourceData);
+ void loadMetaTiles(const ByteArray &resourceData);
+ void loadMulti(const ByteArray &resourceData);
+ void clear();
void draw();
void drawSprite(SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale);
void adjustScroll(bool jump);
@@ -213,16 +212,14 @@ private:
IsoTileData *getTile(int16 u, int16 v, int16 z);
- byte *_tileData;
- size_t _tileDataLength;
+ ByteArray _tileData;
Common::Array<IsoTileData> _tilesTable;
Common::Array<TilePlatformData> _tilePlatformList;
Common::Array<MetaTileData> _metaTileList;
Common::Array<MultiTileEntryData> _multiTable;
- uint16 _multiDataCount;
- int16 *_multiTableData;
+ Common::Array<int16> _multiTableData;
TileMapData _tileMap;
diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
index 7503818319..ab0aa12d18 100644
--- a/engines/saga/itedata.cpp
+++ b/engines/saga/itedata.cpp
@@ -269,7 +269,7 @@ ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT] = {
{ 54, 281, 620, 352, 0, 80, 46, 0 } // Orb of Storms in Dam Lab
};
-FxTable ITE_SfxTable[ITE_SFXCOUNT] = {
+IteFxTable ITE_SfxTable[ITE_SFXCOUNT] = {
{ 14, 127 }, // Door open
{ 15, 127 }, // Door close
{ 16, 63 }, // Rush water (floppy volume: 127)
diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h
index 71041902bc..f0f626a51a 100644
--- a/engines/saga/itedata.h
+++ b/engines/saga/itedata.h
@@ -77,16 +77,16 @@ struct ObjectTableData {
uint16 interactBits;
};
-struct FxTable {
- int res;
- int vol;
+struct IteFxTable {
+ byte res;
+ byte vol;
};
#define ITE_OBJECTCOUNT 39
#define ITE_SFXCOUNT 63
extern ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT];
-extern FxTable ITE_SfxTable[ITE_SFXCOUNT];
+extern IteFxTable ITE_SfxTable[ITE_SFXCOUNT];
extern const char *ITEinterfaceTextStrings[][53];
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index e4a16e27da..f1cdcbce46 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);
@@ -103,6 +117,7 @@ void MusicDriver::send(uint32 b) {
Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_currentVolume = 0;
+ _currentMusicBuffer = NULL;
_driver = new MusicDriver();
_digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
@@ -148,11 +163,13 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
// Just set an XMIDI parser for Mac IHNM for now
_parser = MidiParser::createParser_XMIDI();
} else {
- byte *resourceData;
- size_t resourceSize;
+ ByteArray resourceData;
int resourceId = (_vm->getGameId() == GID_ITE ? 9 : 0);
- _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
- if (!memcmp(resourceData, "FORM", 4)) {
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData);
+ if (resourceData.size() < 4) {
+ error("Music::Music Unable to load midi resource data");
+ }
+ if (!memcmp(resourceData.getBuffer(), "FORM", 4)) {
_parser = MidiParser::createParser_XMIDI();
// ITE had MT32 mapped instruments
_driver->setGM(_vm->getGameId() != GID_ITE);
@@ -161,17 +178,12 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
// ITE with standalone MIDI files is General MIDI
_driver->setGM(_vm->getGameId() == GID_ITE);
}
- free(resourceData);
}
-
+
_parser->setMidiDriver(_driver);
_parser->setTimerRate(_driver->getBaseTempo());
_parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
- _songTableLen = 0;
- _songTable = 0;
-
- _midiMusicData = NULL;
_digitalMusic = false;
}
@@ -182,9 +194,6 @@ Music::~Music() {
delete _driver;
_parser->setMidiDriver(NULL);
delete _parser;
-
- free(_songTable);
- free(_midiMusicData);
}
void Music::musicVolumeGaugeCallback(void *refCon) {
@@ -244,9 +253,7 @@ bool Music::isPlaying() {
void Music::play(uint32 resourceId, MusicFlags flags) {
Audio::SeekableAudioStream *audioStream = NULL;
- byte *resourceData;
- size_t resourceSize;
- uint32 loopStart;
+ uint32 loopStart = 0;
debug(2, "Music::play %d, %d", resourceId, flags);
@@ -378,14 +385,19 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
#endif
return;
} else {
- _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
+ if (_currentMusicBuffer == &_musicBuffer[1]) {
+ _currentMusicBuffer = &_musicBuffer[0];
+ } else {
+ _currentMusicBuffer = &_musicBuffer[1];
+ }
+ _vm->_resource->loadResource(_musicContext, resourceId, *_currentMusicBuffer);
}
- if (resourceSize < 4) {
+ if (_currentMusicBuffer->size() < 4) {
error("Music::play() wrong music resource size");
}
- if (!_parser->loadMusic(resourceData, resourceSize))
+ if (!_parser->loadMusic(_currentMusicBuffer->getBuffer(), _currentMusicBuffer->size()))
error("Music::play() wrong music resource");
_parser->setTrack(0);
@@ -395,9 +407,6 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
// Handle music looping
_parser->property(MidiParser::mpAutoLoop, (flags & MUSIC_LOOP) ? 1 : 0);
-
- free(_midiMusicData);
- _midiMusicData = resourceData;
}
void Music::pause() {
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 5cce3d4c04..b4d868ed78 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);
@@ -106,8 +106,7 @@ public:
void setVolume(int volume, int time = 1);
int getVolume() { return _currentVolume; }
- int32 *_songTable;
- int _songTableLen;
+ Common::Array<int32> _songTable;
private:
SagaEngine *_vm;
@@ -126,11 +125,12 @@ private:
ResourceContext *_digitalMusicContext;
MidiParser *_parser;
- byte *_midiMusicData;
static void musicVolumeGaugeCallback(void *refCon);
static void onTimer(void *refCon);
void musicVolumeGauge();
+ ByteArray *_currentMusicBuffer;
+ ByteArray _musicBuffer[2];
};
} // End of namespace Saga
diff --git a/engines/saga/objectmap.cpp b/engines/saga/objectmap.cpp
index 02179d1d49..61d90cda69 100644
--- a/engines/saga/objectmap.cpp
+++ b/engines/saga/objectmap.cpp
@@ -45,84 +45,58 @@
namespace Saga {
-HitZone::HitZone(MemoryReadStreamEndian *readStream, int index, int sceneNumber): _index(index) {
- int i, j;
- HitZone::ClickArea *clickArea;
- Point *point;
-
+void HitZone::load(SagaEngine *vm, MemoryReadStreamEndian *readStream, int index, int sceneNumber) {
+ _index = index;
_flags = readStream->readByte();
- _clickAreasCount = readStream->readByte();
+ _clickAreas.resize(readStream->readByte());
_rightButtonVerb = readStream->readByte();
readStream->readByte(); // pad
_nameIndex = readStream->readUint16();
_scriptNumber = readStream->readUint16();
- _clickAreas = (HitZone::ClickArea *)malloc(_clickAreasCount * sizeof(*_clickAreas));
-
- if (_clickAreas == NULL) {
- memoryError("HitZone::HitZone");
- }
-
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- clickArea->pointsCount = readStream->readUint16LE();
+ for (ClickAreas::iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ i->resize(readStream->readUint16LE());
- assert(clickArea->pointsCount);
+ assert(!i->empty());
- clickArea->points = (Point *)malloc(clickArea->pointsCount * sizeof(*(clickArea->points)));
- if (clickArea->points == NULL) {
- memoryError("HitZone::HitZone");
- }
-
- for (j = 0; j < clickArea->pointsCount; j++) {
- point = &clickArea->points[j];
- point->x = readStream->readSint16();
- point->y = readStream->readSint16();
+ for (ClickArea::iterator j = i->begin(); j != i->end(); ++j) {
+ j->x = readStream->readSint16();
+ j->y = readStream->readSint16();
// WORKAROUND: bug #1259608: "ITE: Riff ignores command in Ferret merchant center"
// Apparently ITE Mac version has bug in game data. Both ObjectMap and ActionMap
// for exit area are little taller (y = 123) and thus Riff goes to exit
// when clicked on barrel of nails.
- if (sceneNumber == 18 && index == 0 && i == 0 && j == 0 && point->y == 123)
- point->y = 129;
+ if (vm->getGameId() == GID_ITE) {
+ if (sceneNumber == 18 && index == 0 && (i == _clickAreas.begin()) && (j == i->begin()) && j->y == 123) {
+ j->y = 129;
+ }
+ }
}
}
}
-HitZone::~HitZone() {
- for (int i = 0; i < _clickAreasCount; i++) {
- free(_clickAreas[i].points);
- }
- free(_clickAreas);
-}
-
bool HitZone::getSpecialPoint(Point &specialPoint) const {
- int i, pointsCount;
- HitZone::ClickArea *clickArea;
- Point *points;
-
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
- points = clickArea->points;
- if (pointsCount == 1) {
- specialPoint = points[0];
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ if (i->size() == 1) {
+ specialPoint = (*i)[0];
return true;
}
}
return false;
}
+
bool HitZone::hitTest(const Point &testPoint) {
- int i, pointsCount;
- HitZone::ClickArea *clickArea;
- Point *points;
+ const Point *points;
+ uint pointsCount;
if (_flags & kHitZoneEnabled) {
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
- points = clickArea->points;
-
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ pointsCount = i->size();
+ if (pointsCount < 2) {
+ continue;
+ }
+ points = &i->front();
if (pointsCount == 2) {
// Hit-test a box region
if ((testPoint.x >= points[0].x) &&
@@ -132,11 +106,9 @@ bool HitZone::hitTest(const Point &testPoint) {
return true;
}
} else {
- if (pointsCount > 2) {
- // Hit-test a polygon
- if (hitTestPoly(points, pointsCount, testPoint)) {
- return true;
- }
+ // Hit-test a polygon
+ if (hitTestPoly(points, pointsCount, testPoint)) {
+ return true;
}
}
}
@@ -146,26 +118,25 @@ bool HitZone::hitTest(const Point &testPoint) {
#ifdef SAGA_DEBUG
void HitZone::draw(SagaEngine *vm, int color) {
- int i, pointsCount, j;
+ int pointsCount, j;
Location location;
- HitZone::ClickArea *clickArea;
- Point *points;
+ HitZone::ClickArea tmpPoints;
+ const Point *points;
Point specialPoint1;
Point specialPoint2;
- for (i = 0; i < _clickAreasCount; i++) {
- clickArea = &_clickAreas[i];
- pointsCount = clickArea->pointsCount;
+ for (ClickAreas::const_iterator i = _clickAreas.begin(); i != _clickAreas.end(); ++i) {
+ pointsCount = i->size();
+ points = &i->front();
if (vm->_scene->getFlags() & kSceneFlagISO) {
- points = (Point*)malloc(sizeof(Point) * pointsCount);
+ tmpPoints.resize(pointsCount);
for (j = 0; j < pointsCount; j++) {
- location.u() = clickArea->points[j].x;
- location.v() = clickArea->points[j].y;
+ location.u() = points[j].x;
+ location.v() = points[j].y;
location.z = 0;
- vm->_isoMap->tileCoordsToScreenPoint(location, points[j]);
+ vm->_isoMap->tileCoordsToScreenPoint(location, tmpPoints[j]);
}
- } else {
- points = clickArea->points;
+ points = &tmpPoints.front();
}
if (pointsCount == 2) {
@@ -179,10 +150,6 @@ void HitZone::draw(SagaEngine *vm, int color) {
vm->_gfx->drawPolyLine(points, pointsCount, color);
}
}
- if (vm->_scene->getFlags() & kSceneFlagISO) {
- free(points);
- }
-
}
if (getSpecialPoint(specialPoint1)) {
specialPoint2 = specialPoint1;
@@ -196,55 +163,36 @@ void HitZone::draw(SagaEngine *vm, int color) {
#endif
// Loads an object map resource ( objects ( clickareas ( points ) ) )
-void ObjectMap::load(const byte *resourcePointer, size_t resourceLength) {
- int i;
+void ObjectMap::load(const ByteArray &resourceData) {
- if (resourceLength == 0) {
- return;
+ if (!_hitZoneList.empty()) {
+ error("ObjectMap::load _hitZoneList not empty");
}
- if (resourceLength < 4) {
- error("ObjectMap::load wrong resourceLength");
+ if (resourceData.empty()) {
+ return;
}
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
-
- _hitZoneListCount = readS.readSint16();
- if (_hitZoneListCount < 0) {
- error("ObjectMap::load _hitZoneListCount < 0");
+ if (resourceData.size() < 4) {
+ error("ObjectMap::load wrong resourceLength");
}
- if (_hitZoneList)
- error("ObjectMap::load _hitZoneList != NULL");
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
- _hitZoneList = (HitZone **) malloc(_hitZoneListCount * sizeof(HitZone *));
- if (_hitZoneList == NULL) {
- memoryError("ObjectMap::load");
- }
+ _hitZoneList.resize(readS.readUint16());
- for (i = 0; i < _hitZoneListCount; i++) {
- _hitZoneList[i] = new HitZone(&readS, i, _vm->_scene->currentSceneNumber());
+ int idx = 0;
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ i->load(_vm, &readS, idx++, _vm->_scene->currentSceneNumber());
}
}
-void ObjectMap::freeMem() {
- int i;
-
- if (_hitZoneList) {
- for (i = 0; i < _hitZoneListCount; i++) {
- delete _hitZoneList[i];
- }
-
- free(_hitZoneList);
- _hitZoneList = NULL;
- }
- _hitZoneListCount = 0;
+void ObjectMap::clear() {
+ _hitZoneList.clear();
}
-
#ifdef SAGA_DEBUG
void ObjectMap::draw(const Point& testPoint, int color, int color2) {
- int i;
int hitZoneIndex;
char txtBuf[32];
Point pickPoint;
@@ -260,8 +208,8 @@ void ObjectMap::draw(const Point& testPoint, int color, int color2) {
hitZoneIndex = hitTest(pickPoint);
- for (i = 0; i < _hitZoneListCount; i++) {
- _hitZoneList[i]->draw(_vm, (hitZoneIndex == i) ? color2 : color);
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ i->draw(_vm, (hitZoneIndex == i->getIndex()) ? color2 : color);
}
if (hitZoneIndex != -1) {
@@ -274,12 +222,11 @@ void ObjectMap::draw(const Point& testPoint, int color, int color2) {
#endif
int ObjectMap::hitTest(const Point& testPoint) {
- int i;
// Loop through all scene objects
- for (i = 0; i < _hitZoneListCount; i++) {
- if (_hitZoneList[i]->hitTest(testPoint)) {
- return i;
+ for (HitZoneArray::iterator i = _hitZoneList.begin(); i != _hitZoneList.end(); ++i) {
+ if (i->hitTest(testPoint)) {
+ return i->getIndex();
}
}
@@ -287,7 +234,7 @@ int ObjectMap::hitTest(const Point& testPoint) {
}
void ObjectMap::cmdInfo() {
- _vm->_console->DebugPrintf("%d zone(s) loaded.\n\n", _hitZoneListCount);
+ _vm->_console->DebugPrintf("%d zone(s) loaded.\n\n", _hitZoneList.size());
}
} // End of namespace Saga
diff --git a/engines/saga/objectmap.h b/engines/saga/objectmap.h
index df0dcffe57..446afd478e 100644
--- a/engines/saga/objectmap.h
+++ b/engines/saga/objectmap.h
@@ -33,15 +33,14 @@ namespace Saga {
class HitZone {
private:
- struct ClickArea {
- int pointsCount;
- Point *points;
- };
-
+ typedef Common::Array<Point> ClickArea;
+ typedef Common::Array<ClickArea> ClickAreas;
public:
- HitZone(MemoryReadStreamEndian *readStream, int index, int sceneNumber);
- ~HitZone();
+ void load(SagaEngine *vm, MemoryReadStreamEndian *readStream, int index, int sceneNumber);
+ int getIndex() const {
+ return _index;
+ }
int getNameIndex() const {
return _nameIndex;
}
@@ -76,40 +75,38 @@ public:
return objectIndexToId(kGameObjectStepZone, _index);
}
bool getSpecialPoint(Point &specialPoint) const;
+#ifdef SAGA_DEBUG
void draw(SagaEngine *vm, int color); // for debugging
+#endif
bool hitTest(const Point &testPoint);
private:
int _flags; // Saga::HitZoneFlags
- int _clickAreasCount;
int _rightButtonVerb;
int _nameIndex;
int _scriptNumber;
int _index;
- ClickArea *_clickAreas;
+ ClickAreas _clickAreas;
};
+typedef Common::Array<HitZone> HitZoneArray;
class ObjectMap {
public:
ObjectMap(SagaEngine *vm) : _vm(vm) {
- _hitZoneList = NULL;
- _hitZoneListCount = 0;
-
- }
- ~ObjectMap() {
- freeMem();
}
- void load(const byte *resourcePointer, size_t resourceLength);
- void freeMem();
+ void load(const ByteArray &resourceData);
+ void clear();
+#ifdef SAGA_DEBUG
void draw(const Point& testPoint, int color, int color2); // for debugging
+#endif
int hitTest(const Point& testPoint);
HitZone *getHitZone(int16 index) {
- if ((index < 0) || (index >= _hitZoneListCount)) {
+ if (uint(index) >= _hitZoneList.size()) {
return NULL;
}
- return _hitZoneList[index];
+ return &_hitZoneList[index];
}
void cmdInfo();
@@ -117,8 +114,7 @@ public:
private:
SagaEngine *_vm;
- int _hitZoneListCount;
- HitZone **_hitZoneList;
+ HitZoneArray _hitZoneList;
};
} // End of namespace Saga
diff --git a/engines/saga/palanim.cpp b/engines/saga/palanim.cpp
index dc892b845a..b0b76fc947 100644
--- a/engines/saga/palanim.cpp
+++ b/engines/saga/palanim.cpp
@@ -35,126 +35,95 @@
namespace Saga {
PalAnim::PalAnim(SagaEngine *vm) : _vm(vm) {
- _loaded = false;
- _entryCount = 0;
- _entries = NULL;
}
-PalAnim::~PalAnim() {
-}
-
-int PalAnim::loadPalAnim(const byte *resdata, size_t resdata_len) {
- void *test_p;
+void PalAnim::loadPalAnim(const ByteArray &resourceData) {
- uint16 i;
+ clear();
- if (_loaded) {
- freePalAnim();
+ if (resourceData.empty()) {
+ return;
}
- if (resdata == NULL) {
- return FAILURE;
- }
-
- MemoryReadStreamEndian readS(resdata, resdata_len, _vm->isBigEndian());
+ ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
if (_vm->getGameId() == GID_IHNM) {
- return SUCCESS;
+ return;
}
- _entryCount = readS.readUint16();
-
- debug(3, "PalAnim::loadPalAnim(): Loading %d PALANIM entries.", _entryCount);
+ _entries.resize(readS.readUint16());
- test_p = malloc(_entryCount * sizeof(PalanimEntry));
- _entries = (PalanimEntry *)test_p;
+ debug(3, "PalAnim::loadPalAnim(): Loading %d PALANIM entries.", _entries.size());
- for (i = 0; i < _entryCount; i++) {
- int color_count;
- int pal_count;
- int p, c;
+ for (Common::Array<PalanimEntry>::iterator i = _entries.begin(); i != _entries.end(); ++i) {
+
+ i->cycle = 0;
- _entries[i].cycle = 0;
+ i->colors.resize(readS.readUint16());
+ debug(2, "PalAnim::loadPalAnim(): Loading %d SAGA_COLOR structures.", i->colors.size());
- color_count = readS.readUint16();
- pal_count = readS.readUint16();
+ i->palIndex.resize(readS.readUint16());
+ debug(2, "PalAnim::loadPalAnim(): Loading %d palette indices.\n", i->palIndex.size());
- _entries[i].pal_count = pal_count;
- _entries[i].color_count = color_count;
- debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d palette indices.\n", i, pal_count);
-
- test_p = malloc(sizeof(char) * pal_count);
- _entries[i].pal_index = (byte *)test_p;
-
- debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d SAGA_COLOR structures.", i, color_count);
-
- test_p = malloc(sizeof(Color) * color_count);
- _entries[i].colors = (Color *)test_p;
-
- for (p = 0; p < pal_count; p++) {
- _entries[i].pal_index[p] = readS.readByte();
+ for (uint j = 0; j < i->palIndex.size(); j++) {
+ i->palIndex[j] = readS.readByte();
}
- for (c = 0; c < color_count; c++) {
- _entries[i].colors[c].red = readS.readByte();
- _entries[i].colors[c].green = readS.readByte();
- _entries[i].colors[c].blue = readS.readByte();
+ for (Common::Array<Color>::iterator j = i->colors.begin(); j != i->colors.end(); ++j) {
+ j->red = readS.readByte();
+ j->green = readS.readByte();
+ j->blue = readS.readByte();
}
}
-
- _loaded = true;
- return SUCCESS;
}
-int PalAnim::cycleStart() {
+void PalAnim::cycleStart() {
Event event;
- if (!_loaded) {
- return FAILURE;
+ if (_entries.empty()) {
+ return;
}
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStep;
event.time = PALANIM_CYCLETIME;
- _vm->_events->queue(&event);
-
- return SUCCESS;
+ _vm->_events->queue(event);
}
-int PalAnim::cycleStep(int vectortime) {
+void PalAnim::cycleStep(int vectortime) {
static PalEntry pal[256];
- uint16 pal_index;
- uint16 col_index;
+ uint16 palIndex;
+ uint16 colIndex;
- uint16 i, j;
+ uint16 j;
uint16 cycle;
- uint16 cycle_limit;
+ uint16 cycleLimit;
Event event;
- if (!_loaded) {
- return FAILURE;
+ if (_entries.empty()) {
+ return;
}
_vm->_gfx->getCurrentPal(pal);
- for (i = 0; i < _entryCount; i++) {
- cycle = _entries[i].cycle;
- cycle_limit = _entries[i].color_count;
- for (j = 0; j < _entries[i].pal_count; j++) {
- pal_index = (unsigned char)_entries[i].pal_index[j];
- col_index = (cycle + j) % cycle_limit;
- pal[pal_index].red = (byte) _entries[i].colors[col_index].red;
- pal[pal_index].green = (byte) _entries[i].colors[col_index].green;
- pal[pal_index].blue = (byte) _entries[i].colors[col_index].blue;
+ for (Common::Array<PalanimEntry>::iterator i = _entries.begin(); i != _entries.end(); ++i) {
+ cycle = i->cycle;
+ cycleLimit = i->colors.size();
+ for (j = 0; j < i->palIndex.size(); j++) {
+ palIndex = i->palIndex[j];
+ colIndex = (cycle + j) % cycleLimit;
+ pal[palIndex].red = (byte) i->colors[colIndex].red;
+ pal[palIndex].green = (byte) i->colors[colIndex].green;
+ pal[palIndex].blue = (byte) i->colors[colIndex].blue;
}
- _entries[i].cycle++;
+ i->cycle++;
- if (_entries[i].cycle == cycle_limit) {
- _entries[i].cycle = 0;
+ if (i->cycle == cycleLimit) {
+ i->cycle = 0;
}
}
@@ -167,32 +136,14 @@ int PalAnim::cycleStep(int vectortime) {
event.code = kPalAnimEvent;
event.op = kEventCycleStep;
event.time = vectortime + PALANIM_CYCLETIME;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
- return SUCCESS;
}
-int PalAnim::freePalAnim() {
- uint16 i;
-
- if (!_loaded) {
- return FAILURE;
- }
-
- for (i = 0; i < _entryCount; i++) {
- debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing colors.", i);
- free(_entries[i].colors);
- debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing indices.", i);
- free(_entries[i].pal_index);
- }
-
- debug(3, "PalAnim::freePalAnim(): Freeing entries.");
-
- free(_entries);
-
- _loaded = false;
-
- return SUCCESS;
+void PalAnim::clear() {
+ debug(3, "PalAnim::clear()");
+
+ _entries.clear();
}
} // End of namespace Saga
diff --git a/engines/saga/palanim.h b/engines/saga/palanim.h
index 52002e01c3..2d2c3f1399 100644
--- a/engines/saga/palanim.h
+++ b/engines/saga/palanim.h
@@ -33,29 +33,24 @@ namespace Saga {
#define PALANIM_CYCLETIME 100
struct PalanimEntry {
- uint16 pal_count;
- uint16 color_count;
uint16 cycle;
- byte *pal_index;
- Color *colors;
+ ByteArray palIndex;
+ Common::Array<Color> colors;
};
class PalAnim {
public:
PalAnim(SagaEngine *vm);
- ~PalAnim();
- int loadPalAnim(const byte *, size_t);
- int cycleStart();
- int cycleStep(int vectortime);
- int freePalAnim();
+ void loadPalAnim(const ByteArray &resourceData);
+ void cycleStart();
+ void cycleStep(int vectortime);
+ void clear();
private:
SagaEngine *_vm;
- bool _loaded;
- uint16 _entryCount;
- PalanimEntry *_entries;
+ Common::Array<PalanimEntry> _entries;
};
} // End of namespace Saga
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 5b13473d77..af81c3c670 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -172,7 +172,7 @@ void Puzzle::initPieces() {
_vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
for (int i = 0; i < PUZZLE_PIECES; i++) {
- spI = &(spriteList->infoList[i]);
+ spI = &((*spriteList)[i]);
_pieceInfo[i].offX = (byte)(spI->width >> 1);
_pieceInfo[i].offY = (byte)(spI->height >> 1);
@@ -347,7 +347,7 @@ void Puzzle::dropPiece(Point mousePt) {
if (newy < boxy)
newy = PUZZLE_Y_OFFSET;
- spI = &(spriteList->infoList[_puzzlePiece]);
+ spI = &((*spriteList)[_puzzlePiece]);
if (newx + spI->width > boxw)
newx = boxw - spI->width ;
@@ -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..0fb7327f0d 100644
--- a/engines/saga/resource.cpp
+++ b/engines/saga/resource.cpp
@@ -43,8 +43,7 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
size_t i;
bool result;
byte tableInfo[RSC_TABLEINFO_SIZE];
- byte *tableBuffer;
- size_t tableSize;
+ ByteArray tableBuffer;
uint32 count;
uint32 resourceTableOffset;
ResourceData *resourceData;
@@ -70,17 +69,15 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
}
// Load resource table
- tableSize = RSC_TABLEENTRY_SIZE * count;
-
- tableBuffer = (byte *)malloc(tableSize);
+ tableBuffer.resize(RSC_TABLEENTRY_SIZE * count);
_file.seek(resourceTableOffset + contextOffset, SEEK_SET);
- result = (_file.read(tableBuffer, tableSize) == tableSize);
+ result = (_file.read(tableBuffer.getBuffer(), tableBuffer.size()) == tableBuffer.size());
if (result) {
_table.resize(count);
- MemoryReadStreamEndian readS1(tableBuffer, tableSize, _isBigEndian);
+ MemoryReadStreamEndian readS1(tableBuffer.getBuffer(), tableBuffer.size(), _isBigEndian);
for (i = 0; i < count; i++) {
resourceData = &_table[i];
@@ -94,7 +91,6 @@ bool ResourceContext::loadResV1(uint32 contextOffset, uint32 contextSize) {
}
}
- free(tableBuffer);
return result;
}
@@ -107,8 +103,6 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
uint32 subjectResourceId;
uint32 patchResourceId;
ResourceData *subjectResourceData;
- byte *tableBuffer;
- size_t tableSize;
bool isMacBinary;
if (_fileName == NULL) { // IHNM special case
@@ -145,10 +139,12 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
if (subjectContext == NULL) {
error("ResourceContext::load() Subject context not found");
}
- resource->loadResource(this, _table.size() - 1, tableBuffer, tableSize);
+ ByteArray tableBuffer;
+
+ resource->loadResource(this, _table.size() - 1, tableBuffer);
- MemoryReadStreamEndian readS2(tableBuffer, tableSize, _isBigEndian);
- for (i = 0; i < tableSize / 8; i++) {
+ ByteArrayReadStreamEndian readS2(tableBuffer, _isBigEndian);
+ for (i = 0; i < tableBuffer.size() / 8; i++) {
subjectResourceId = readS2.readUint32();
patchResourceId = readS2.readUint32();
subjectResourceData = subjectContext->getResourceData(subjectResourceId);
@@ -157,7 +153,6 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
subjectResourceData->offset = resourceData->offset;
subjectResourceData->size = resourceData->size;
}
- free(tableBuffer);
}
//process external patch files
@@ -165,18 +160,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 +220,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;
}
@@ -367,25 +365,25 @@ void Resource::clearContexts() {
}
}
-void Resource::loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize) {
+void Resource::loadResource(ResourceContext *context, uint32 resourceId, ByteArray &resourceBuffer) {
Common::File *file;
uint32 resourceOffset;
ResourceData *resourceData;
- debug(8, "loadResource %d", resourceId);
resourceData = context->getResourceData(resourceId);
file = context->getFile(resourceData);
resourceOffset = resourceData->offset;
- resourceSize = resourceData->size;
- resourceBuffer = (byte*)malloc(resourceSize);
+ debug(8, "loadResource %d 0x%X:0x%X", resourceId, resourceOffset, uint(resourceData->size));
+ resourceBuffer.resize(resourceData->size);
+
file->seek((long)resourceOffset, SEEK_SET);
- if (file->read(resourceBuffer, resourceSize) != resourceSize) {
+ if (file->read(resourceBuffer.getBuffer(), resourceBuffer.size()) != resourceBuffer.size()) {
error("Resource::loadResource() failed to read");
}
diff --git a/engines/saga/resource.h b/engines/saga/resource.h
index e32d16c469..5009c862f4 100644
--- a/engines/saga/resource.h
+++ b/engines/saga/resource.h
@@ -206,7 +206,7 @@ public:
virtual ~Resource();
bool createContexts();
void clearContexts();
- void loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize);
+ void loadResource(ResourceContext *context, uint32 resourceId, ByteArray &resourceBuffer);
virtual uint32 convertResourceId(uint32 resourceId) = 0;
virtual void loadGlobalResources(int chapter, int actorsEntrance) = 0;
diff --git a/engines/saga/resource_res.cpp b/engines/saga/resource_res.cpp
index 8546030241..646de8667b 100644
--- a/engines/saga/resource_res.cpp
+++ b/engines/saga/resource_res.cpp
@@ -48,13 +48,13 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
if (chapter < 0)
chapter = !_vm->isIHNMDemo() ? 8 : 7;
- _vm->_script->_globalVoiceLUT.freeMem();
+ _vm->_script->_globalVoiceLUT.clear();
// TODO: close chapter context, or rather reassign it in our case
ResourceContext *resourceContext;
ResourceContext *soundContext;
- int i;
+ uint i;
resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
if (resourceContext == NULL) {
@@ -66,41 +66,38 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
error("Resource::loadGlobalResources() sound context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
if (!_vm->isIHNMDemo()) {
- _vm->_resource->loadResource(resourceContext, metaResourceTable[chapter],
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, metaResourceTable[chapter], resourceData);
} else {
- _vm->_resource->loadResource(resourceContext, metaResourceTableDemo[chapter],
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, metaResourceTableDemo[chapter], resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources wrong metaResource");
}
- MemoryReadStream metaS(resourcePointer, resourceLength);
-
- _metaResource.sceneIndex = metaS.readSint16LE();
- _metaResource.objectCount = metaS.readSint16LE();
- _metaResource.objectsStringsResourceID = metaS.readSint32LE();
- _metaResource.inventorySpritesID = metaS.readSint32LE();
- _metaResource.mainSpritesID = metaS.readSint32LE();
- _metaResource.objectsResourceID = metaS.readSint32LE();
- _metaResource.actorCount = metaS.readSint16LE();
- _metaResource.actorsStringsResourceID = metaS.readSint32LE();
- _metaResource.actorsResourceID = metaS.readSint32LE();
- _metaResource.protagFaceSpritesID = metaS.readSint32LE();
- _metaResource.field_22 = metaS.readSint32LE();
- _metaResource.field_26 = metaS.readSint16LE();
- _metaResource.protagStatesCount = metaS.readSint16LE();
- _metaResource.protagStatesResourceID = metaS.readSint32LE();
- _metaResource.cutawayListResourceID = metaS.readSint32LE();
- _metaResource.songTableID = metaS.readSint32LE();
-
- free(resourcePointer);
+ {
+ ByteArrayReadStreamEndian metaS(resourceData);
+
+ _metaResource.sceneIndex = metaS.readSint16LE();
+ _metaResource.objectCount = metaS.readSint16LE();
+ _metaResource.objectsStringsResourceID = metaS.readSint32LE();
+ _metaResource.inventorySpritesID = metaS.readSint32LE();
+ _metaResource.mainSpritesID = metaS.readSint32LE();
+ _metaResource.objectsResourceID = metaS.readSint32LE();
+ _metaResource.actorCount = metaS.readSint16LE();
+ _metaResource.actorsStringsResourceID = metaS.readSint32LE();
+ _metaResource.actorsResourceID = metaS.readSint32LE();
+ _metaResource.protagFaceSpritesID = metaS.readSint32LE();
+ _metaResource.field_22 = metaS.readSint32LE();
+ _metaResource.field_26 = metaS.readSint16LE();
+ _metaResource.protagStatesCount = metaS.readSint16LE();
+ _metaResource.protagStatesResourceID = metaS.readSint32LE();
+ _metaResource.cutawayListResourceID = metaS.readSint32LE();
+ _metaResource.songTableID = metaS.readSint32LE();
+ }
_vm->_actor->loadActorList(actorsEntrance, _metaResource.actorCount,
_metaResource.actorsResourceID, _metaResource.protagStatesCount,
@@ -108,90 +105,83 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
_vm->_actor->_protagonist->_sceneNumber = _metaResource.sceneIndex;
- _vm->_actor->_objectsStrings.freeMem();
+ _vm->_actor->_objectsStrings.clear();
- _vm->_resource->loadResource(resourceContext, _metaResource.objectsStringsResourceID, resourcePointer, resourceLength);
- _vm->loadStrings(_vm->_actor->_objectsStrings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, _metaResource.objectsStringsResourceID, resourceData);
+ _vm->loadStrings(_vm->_actor->_objectsStrings, resourceData);
- if (chapter >= _vm->_sndRes->_fxTableIDsLen) {
+ if (uint(chapter) >= _vm->_sndRes->_fxTableIDs.size()) {
error("Chapter ID exceeds fxTableIDs length");
}
debug(0, "Going to read %d of %d", chapter, _vm->_sndRes->_fxTableIDs[chapter]);
_vm->_resource->loadResource(soundContext, _vm->_sndRes->_fxTableIDs[chapter],
- resourcePointer, resourceLength);
+ resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load sound effects for current track");
}
- free(_vm->_sndRes->_fxTable);
-
- _vm->_sndRes->_fxTableLen = resourceLength / 4;
- _vm->_sndRes->_fxTable = (FxTable *)malloc(sizeof(FxTable) * _vm->_sndRes->_fxTableLen);
+ _vm->_sndRes->_fxTable.resize(resourceData.size() / 4);
- MemoryReadStream fxS(resourcePointer, resourceLength);
+ {
+ ByteArrayReadStreamEndian fxS(resourceData);
- for (i = 0; i < _vm->_sndRes->_fxTableLen; i++) {
- _vm->_sndRes->_fxTable[i].res = fxS.readSint16LE();
- _vm->_sndRes->_fxTable[i].vol = fxS.readSint16LE();
+ for (i = 0; i < _vm->_sndRes->_fxTable.size(); i++) {
+ _vm->_sndRes->_fxTable[i].res = fxS.readSint16LE();
+ _vm->_sndRes->_fxTable[i].vol = fxS.readSint16LE();
+ }
}
- free(resourcePointer);
- _vm->_interface->_defPortraits.freeMem();
+ _vm->_interface->_defPortraits.clear();
_vm->_sprite->loadList(_metaResource.protagFaceSpritesID, _vm->_interface->_defPortraits);
- _vm->_actor->_actorsStrings.freeMem();
+ _vm->_actor->_actorsStrings.clear();
- _vm->_resource->loadResource(resourceContext, _metaResource.actorsStringsResourceID, resourcePointer, resourceLength);
- _vm->loadStrings(_vm->_actor->_actorsStrings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, _metaResource.actorsStringsResourceID, resourceData);
+ _vm->loadStrings(_vm->_actor->_actorsStrings, resourceData);
- _vm->_sprite->_inventorySprites.freeMem();
+ _vm->_sprite->_inventorySprites.clear();
_vm->_sprite->loadList(_metaResource.inventorySpritesID, _vm->_sprite->_inventorySprites);
- _vm->_sprite->_mainSprites.freeMem();
+ _vm->_sprite->_mainSprites.clear();
_vm->_sprite->loadList(_metaResource.mainSpritesID, _vm->_sprite->_mainSprites);
_vm->_actor->loadObjList(_metaResource.objectCount, _metaResource.objectsResourceID);
- _vm->_resource->loadResource(resourceContext, _metaResource.cutawayListResourceID, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, _metaResource.cutawayListResourceID, resourceData);
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load cutaway list");
}
- _vm->_anim->loadCutawayList(resourcePointer, resourceLength);
+ _vm->_anim->loadCutawayList(resourceData);
if (_metaResource.songTableID > 0) {
- _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourceData);
if (chapter == 6) {
- int32 id = READ_LE_UINT32(&resourcePointer[actorsEntrance * 4]);
- free(resourcePointer);
- _vm->_resource->loadResource(resourceContext, id, resourcePointer, resourceLength);
+ if (resourceData.size() < (uint(actorsEntrance) * 4 + 4)) {
+ error("Resource::loadGlobalResources chapter 6 has wrong resource");
+ }
+ int32 id = READ_LE_UINT32(&resourceData[actorsEntrance * 4]);
+ _vm->_resource->loadResource(resourceContext, id, resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Resource::loadGlobalResources Can't load songs list for current track");
}
- free(_vm->_music->_songTable);
-
- _vm->_music->_songTableLen = resourceLength / 4;
- _vm->_music->_songTable = (int32 *)malloc(sizeof(int32) * _vm->_music->_songTableLen);
+ _vm->_music->_songTable.resize(resourceData.size() / 4);
- MemoryReadStream songS(resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian songS(resourceData);
- for (i = 0; i < _vm->_music->_songTableLen; i++)
+ for (i = 0; i < _vm->_music->_songTable.size(); i++)
_vm->_music->_songTable[i] = songS.readSint32LE();
- free(resourcePointer);
} else {
// The IHNM demo has a fixed music track and doesn't load a song table
_vm->_music->setVolume(_vm->_musicVolume, 1);
_vm->_music->play(3, MUSIC_LOOP);
- free(resourcePointer);
}
int voiceLUTResourceID = 0;
@@ -207,9 +197,8 @@ void Resource_RES::loadGlobalResources(int chapter, int actorsEntrance) {
}
if (voiceLUTResourceID) {
- _vm->_resource->loadResource(resourceContext, voiceLUTResourceID, resourcePointer, resourceLength);
- _vm->_script->loadVoiceLUT(_vm->_script->_globalVoiceLUT, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->_resource->loadResource(resourceContext, voiceLUTResourceID, resourceData);
+ _vm->_script->loadVoiceLUT(_vm->_script->_globalVoiceLUT, resourceData);
}
_vm->_spiritualBarometer = 0;
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 1b7fa97f8d..abd681ce87 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -291,7 +291,7 @@ Common::Error SagaEngine::run() {
_sound = new Sound(this, _mixer);
if (!isSaga2()) {
- _interface->converseInit();
+ _interface->converseClear();
_script->setVerb(_script->getVerbType(kVerbWalkTo));
}
@@ -393,28 +393,26 @@ Common::Error SagaEngine::run() {
return Common::kNoError;
}
-void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength) {
+void SagaEngine::loadStrings(StringsTable &stringsTable, const ByteArray &stringsData) {
uint16 stringsCount;
size_t offset;
size_t prevOffset = 0;
- int i;
+ Common::Array<size_t> tempOffsets;
+ uint ui;
- if (stringsLength == 0) {
+ if (stringsData.empty()) {
error("SagaEngine::loadStrings() Error loading strings list resource");
}
- stringsTable.stringsPointer = (byte*)malloc(stringsLength);
- memcpy(stringsTable.stringsPointer, stringsPointer, stringsLength);
-
- MemoryReadStreamEndian scriptS(stringsTable.stringsPointer, stringsLength, isBigEndian()); //TODO: get endianess from context
+ ByteArrayReadStreamEndian scriptS(stringsData, isBigEndian()); //TODO: get endianess from context
offset = scriptS.readUint16();
stringsCount = offset / 2;
- stringsTable.strings = (const char **)malloc(stringsCount * sizeof(*stringsTable.strings));
- i = 0;
+ ui = 0;
scriptS.seek(0);
- while (i < stringsCount) {
+ tempOffsets.resize(stringsCount);
+ while (ui < stringsCount) {
offset = scriptS.readUint16();
// In some rooms in IHNM, string offsets can be greater than the maximum value than a 16-bit integer can hold
// We detect this by checking the previous offset, and if it was bigger than the current one, an overflow
@@ -423,27 +421,47 @@ void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPoin
if (prevOffset > offset)
offset += 65536;
prevOffset = offset;
- if (offset == stringsLength) {
- stringsCount = i;
- stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
+ if (offset == stringsData.size()) {
+ stringsCount = ui;
+ tempOffsets.resize(stringsCount);
break;
}
- if (offset > stringsLength) {
+ if (offset > stringsData.size()) {
// This case should never occur, but apparently it does in the Italian fan
// translation of IHNM
warning("SagaEngine::loadStrings wrong strings table");
- stringsCount = i;
- stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
+ stringsCount = ui;
+ tempOffsets.resize(stringsCount);
break;
}
- stringsTable.strings[i] = (const char *)stringsTable.stringsPointer + offset;
- debug(9, "string[%i]=%s", i, stringsTable.strings[i]);
- i++;
+ tempOffsets[ui] = offset;
+ ui++;
+ }
+
+ prevOffset = scriptS.pos();
+ int32 left = scriptS.size() - prevOffset;
+ if (left < 0) {
+ error("SagaEngine::loadStrings() Error loading strings buffer");
+ }
+
+ stringsTable.buffer.resize(left);
+ if (left > 0) {
+ scriptS.read(&stringsTable.buffer.front(), left);
+ }
+
+ stringsTable.strings.resize(tempOffsets.size());
+ for (ui = 0; ui < tempOffsets.size(); ui++) {
+ offset = tempOffsets[ui] - prevOffset;
+ if (offset >= stringsTable.buffer.size()) {
+ error("SagaEngine::loadStrings() Wrong offset");
+ }
+ stringsTable.strings[ui] = &stringsTable.buffer[offset];
+
+ debug(9, "string[%i]=%s", ui, stringsTable.strings[ui]);
}
- stringsTable.stringsCount = stringsCount;
}
-const char *SagaEngine::getObjectName(uint16 objectId) {
+const char *SagaEngine::getObjectName(uint16 objectId) const {
ActorData *actor;
ObjectData *obj;
const HitZone *hitZone;
@@ -598,7 +616,7 @@ void SagaEngine::setTalkspeed(int talkspeed) {
ConfMan.setInt("talkspeed", (talkspeed * 255 + 3 / 2) / 3);
}
-int SagaEngine::getTalkspeed() {
+int SagaEngine::getTalkspeed() const {
return (ConfMan.getInt("talkspeed") * 3 + 255 / 2) / 255;
}
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 102d1e5c82..8eb4833278 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -49,7 +49,7 @@ struct ADGameFileDescription;
* SAGA2 status: in early stages of development, no recent activity. Contact sev
* if you want to work on it, since we have some original source codes.
*
- * Supported games:
+ * Games using this engine:
*
* SAGA:
* - Inherit the Earth
@@ -86,7 +86,7 @@ class ResourceContext;
using Common::MemoryReadStream;
using Common::MemoryReadStreamEndian;
-//#define SAGA_DEBUG 1 // define for test functions
+// #define SAGA_DEBUG 1 // define for test functions
#define SAGA_IMAGE_DATA_OFFSET 776
#define SAGA_IMAGE_HEADER_LEN 8
@@ -155,40 +155,40 @@ enum GameFeatures {
};
enum VerbTypeIds {
- kVerbITENone = 0,
- kVerbITEPickUp = 1,
- kVerbITELookAt = 2,
- kVerbITEWalkTo = 3,
- kVerbITETalkTo = 4,
- kVerbITEOpen = 5,
- kVerbITEClose = 6,
- kVerbITEGive = 7,
- kVerbITEUse = 8,
- kVerbITEOptions = 9,
- kVerbITEEnter = 10,
- kVerbITELeave = 11,
- kVerbITEBegin = 12,
- kVerbITEWalkOnly = 13,
- kVerbITELookOnly = 14,
-
-
- kVerbIHNMNone = 0,
- kVerbIHNMWalk = 1,
- kVerbIHNMLookAt = 2,
- kVerbIHNMTake = 3,
- kVerbIHNMUse = 4,
- kVerbIHNMTalkTo = 5,
- kVerbIHNMSwallow = 6,
- kVerbIHNMGive = 7,
- kVerbIHNMPush = 8,
- kVerbIHNMOptions = 9,
- kVerbIHNMEnter = 10,
- kVerbIHNMLeave = 11,
- kVerbIHNMBegin = 12,
- kVerbIHNMWalkOnly = 13,
- kVerbIHNMLookOnly = 14,
-
- kVerbTypeIdsMax = kVerbITELookOnly + 1
+ kVerbITENone = 0,
+ kVerbITEPickUp = 1,
+ kVerbITELookAt = 2,
+ kVerbITEWalkTo = 3,
+ kVerbITETalkTo = 4,
+ kVerbITEOpen = 5,
+ kVerbITEClose = 6,
+ kVerbITEGive = 7,
+ kVerbITEUse = 8,
+ kVerbITEOptions = 9,
+ kVerbITEEnter = 10,
+ kVerbITELeave = 11,
+ kVerbITEBegin = 12,
+ kVerbITEWalkOnly = 13,
+ kVerbITELookOnly = 14,
+
+
+ kVerbIHNMNone = 0,
+ kVerbIHNMWalk = 1,
+ kVerbIHNMLookAt = 2,
+ kVerbIHNMTake = 3,
+ kVerbIHNMUse = 4,
+ kVerbIHNMTalkTo = 5,
+ kVerbIHNMSwallow = 6,
+ kVerbIHNMGive = 7,
+ kVerbIHNMPush = 8,
+ kVerbIHNMOptions = 9,
+ kVerbIHNMEnter = 10,
+ kVerbIHNMLeave = 11,
+ kVerbIHNMBegin = 12,
+ kVerbIHNMWalkOnly = 13,
+ kVerbIHNMLookOnly = 14,
+
+ kVerbTypeIdsMax = kVerbITELookOnly + 1
};
enum PanelButtonType {
@@ -381,30 +381,21 @@ struct ImageHeader {
};
struct StringsTable {
- byte *stringsPointer;
- int stringsCount;
- const char **strings;
+ Common::Array<char> buffer;
+ Common::Array<char *> strings;
- const char *getString(int index) const {
- if ((stringsCount <= index) || (index < 0)) {
+ const char *getString(uint index) const {
+ if (strings.size() <= index) {
// This occurs at the end of Ted's chapter, right after the ending cutscene
- warning("StringsTable::getString wrong index 0x%X (%d)", index, stringsCount);
+ warning("StringsTable::getString wrong index 0x%X (%d)", index, strings.size());
return "";
}
return strings[index];
}
- void freeMem() {
- free(strings);
- free(stringsPointer);
- memset(this, 0, sizeof(*this));
- }
-
- StringsTable() {
- memset(this, 0, sizeof(*this));
- }
- ~StringsTable() {
- freeMem();
+ void clear() {
+ strings.clear();
+ buffer.clear();
}
};
@@ -467,6 +458,34 @@ inline uint16 objectIndexToId(int type, int index) {
return (type << OBJECT_TYPE_SHIFT) | (OBJECT_TYPE_MASK & index);
}
+class ByteArray : public Common::Array<byte> {
+public:
+ /**
+ * Return a pointer to the start of the buffer underlying this byte array,
+ * or NULL if the buffer is empty.
+ */
+ byte *getBuffer() {
+ return empty() ? NULL : &front();
+ }
+
+ const byte *getBuffer() const {
+ return empty() ? NULL : &front();
+ }
+
+ void assign(const ByteArray &src) {
+ resize(src.size());
+ if (!empty()) {
+ memcpy(&front(), &src.front(), size());
+ }
+ }
+};
+
+class ByteArrayReadStreamEndian : public MemoryReadStreamEndian {
+public:
+ ByteArrayReadStreamEndian(const ByteArray & byteArray, bool bigEndian = false) : MemoryReadStreamEndian(byteArray.getBuffer(), byteArray.size(), bigEndian) {
+ }
+};
+
class SagaEngine : public Engine {
friend class Scene;
@@ -484,15 +503,14 @@ public:
void save(const char *fileName, const char *saveName);
void load(const char *fileName);
- uint32 getCurrentLoadVersion() {
+ uint32 getCurrentLoadVersion() const {
return _saveHeader.version;
}
void fillSaveList();
char *calcSaveFileName(uint slotNumber);
SaveFileData *getSaveFile(uint idx);
- uint getSaveSlotNumber(uint idx);
- uint getNewSaveSlotNumber();
+ uint getNewSaveSlotNumber() const;
bool locateSaveFile(char *saveName, uint &titleNumber);
bool isSaveListFull() const {
return _saveFilesCount == MAX_SAVES;
@@ -501,7 +519,7 @@ public:
return isSaveListFull() ? _saveFilesCount : _saveFilesCount + 1;
}
- bool isIHNMDemo() { return _isIHNMDemo; }
+ bool isIHNMDemo() const { return _isIHNMDemo; }
int16 _framesEsc;
@@ -546,23 +564,28 @@ public:
Common::RandomSource _rnd;
private:
- int decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len);
- int flipImage(byte *img_buf, int columns, int scanlines);
- int unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
+ bool decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, ByteArray &outbuf);
+ void flipImage(byte *imageBuffer, int columns, int scanlines);
+ void unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
uint32 _previousTicks;
public:
- int decodeBGImage(const byte *image_data, size_t image_size,
- byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip = false);
- const byte *getImagePal(const byte *image_data, size_t image_size);
- void loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength);
+ bool decodeBGImage(const ByteArray &imageData, ByteArray &outputBuffer, int *w, int *h, bool flip = false);
+ const byte *getImagePal(const ByteArray &imageData) {
+ if (imageData.size() <= SAGA_IMAGE_HEADER_LEN) {
+ return NULL;
+ }
+
+ return &imageData.front() + SAGA_IMAGE_HEADER_LEN;
+ }
+ void loadStrings(StringsTable &stringsTable, const ByteArray &stringsData);
- const char *getObjectName(uint16 objectId);
+ const char *getObjectName(uint16 objectId) const;
public:
int processInput();
Point mousePos() const;
- int getMouseClickCount() {
+ int getMouseClickCount() const {
return _mouseClickCount;
}
@@ -586,7 +609,7 @@ public:
return _leftMouseButtonPressed || _rightMouseButtonPressed;
}
- inline int ticksToMSec(int tick) {
+ inline int ticksToMSec(int tick) const {
if (getGameId() == GID_ITE)
return tick * 1000 / kScriptTimeTicksPerSecond;
else
@@ -617,9 +640,9 @@ public:
bool isBigEndian() const;
bool isMacResources() const;
bool isSaga2() const { return getGameId() == GID_DINO || getGameId() == GID_FTA2; }
- const GameResourceDescription *getResourceDescription();
+ const GameResourceDescription *getResourceDescription() const;
- const GameFontDescription *getFontDescription(int index);
+ const GameFontDescription *getFontDescription(int index) const;
int getFontsCount() const;
int getGameId() const;
@@ -648,7 +671,7 @@ private:
public:
ColorId KnownColor2ColorId(KnownColor knownColor);
void setTalkspeed(int talkspeed);
- int getTalkspeed();
+ int getTalkspeed() const;
};
} // End of namespace Saga
diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp
index 2740462dab..c5388d6878 100644
--- a/engines/saga/saveload.cpp
+++ b/engines/saga/saveload.cpp
@@ -82,7 +82,7 @@ bool SagaEngine::locateSaveFile(char *saveName, uint &titleNumber) {
return false;
}
-uint SagaEngine::getNewSaveSlotNumber() {
+uint SagaEngine::getNewSaveSlotNumber() const {
uint i, j;
bool found;
for (i = 0; i < MAX_SAVES; i++) {
@@ -240,9 +240,9 @@ void SagaEngine::save(const char *fileName, const char *saveName) {
_actor->saveState(out);
- out->writeSint16LE(_script->_commonBufferSize);
+ out->writeSint16LE(_script->_commonBuffer.size());
- out->write(_script->_commonBuffer, _script->_commonBufferSize);
+ out->write(_script->_commonBuffer.getBuffer(), _script->_commonBuffer.size());
// ISO map x, y coordinates for ITE
if (getGameId() == GID_ITE) {
@@ -282,7 +282,7 @@ void SagaEngine::load(const char *fileName) {
_saveHeader.version = SWAP_BYTES_32(_saveHeader.version);
}
- debug(2, "Save version: %x", _saveHeader.version);
+ debug(2, "Save version: 0x%X", _saveHeader.version);
if (_saveHeader.version < 4)
warning("This savegame is not endian-safe. There may be problems");
@@ -351,7 +351,8 @@ void SagaEngine::load(const char *fileName) {
_actor->loadState(in);
commonBufferSize = in->readSint16LE();
- in->read(_script->_commonBuffer, commonBufferSize);
+ _script->_commonBuffer.resize(commonBufferSize);
+ in->read(_script->_commonBuffer.getBuffer(), commonBufferSize);
if (getGameId() == GID_ITE) {
mapx = in->readSint16LE();
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
index 2887d79693..8e9e4463ba 100644
--- a/engines/saga/scene.cpp
+++ b/engines/saga/scene.cpp
@@ -137,10 +137,9 @@ const char *SAGAResourceTypesString[] = {
};
Scene::Scene(SagaEngine *vm) : _vm(vm) {
- byte *sceneLUTPointer;
- size_t sceneLUTLength;
+ ByteArray sceneLUTData;
uint32 resourceId;
- int i;
+ uint i;
// Do nothing for SAGA2 games for now
if (_vm->isSaga2()) {
@@ -158,74 +157,62 @@ Scene::Scene(SagaEngine *vm) : _vm(vm) {
// Load scene lookup table
resourceId = _vm->_resource->convertResourceId(_vm->getResourceDescription()->sceneLUTResourceId);
debug(3, "Loading scene LUT from resource %i", resourceId);
- _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTPointer, sceneLUTLength);
- if (sceneLUTLength == 0) {
- error("Scene::Scene() sceneLUTLength == 0");
- }
- _sceneCount = sceneLUTLength / 2;
- _sceneLUT = (int *)malloc(_sceneCount * sizeof(*_sceneLUT));
- if (_sceneLUT == NULL) {
- memoryError("Scene::Scene()");
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTData);
+ if (sceneLUTData.empty()) {
+ error("Scene::Scene() sceneLUT is empty");
}
+ _sceneLUT.resize(sceneLUTData.size() / 2);
- MemoryReadStreamEndian readS(sceneLUTPointer, sceneLUTLength, _sceneContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(sceneLUTData, _sceneContext->isBigEndian());
- for (i = 0; i < _sceneCount; i++) {
+ for (i = 0; i < _sceneLUT.size(); i++) {
_sceneLUT[i] = readS.readUint16();
debug(8, "sceneNumber %i has resourceId %i", i, _sceneLUT[i]);
}
- free(sceneLUTPointer);
-
#ifdef SAGA_DEBUG
#define DUMP_SCENES_LEVEL 10
if (DUMP_SCENES_LEVEL <= gDebugLevel) {
- uint j;
int backUpDebugLevel = gDebugLevel;
SAGAResourceTypes *types;
int typesCount;
SAGAResourceTypes resType;
+ SceneResourceDataArray resourceList;
getResourceTypes(types, typesCount);
- for (i = 0; i < _sceneCount; i++) {
+ for (i = 0; i < _sceneLUT.size(); i++) {
gDebugLevel = -1;
loadSceneDescriptor(_sceneLUT[i]);
- loadSceneResourceList(_sceneDescription.resourceListResourceId);
+ loadSceneResourceList(_sceneDescription.resourceListResourceId, resourceList);
gDebugLevel = backUpDebugLevel;
debug(DUMP_SCENES_LEVEL, "Dump Scene: number %i, descriptor resourceId %i, resourceList resourceId %i", i, _sceneLUT[i], _sceneDescription.resourceListResourceId);
- debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", (int)_resourceListCount);
- for (j = 0; j < _resourceListCount; j++) {
- if (_resourceList[j].resourceType >= typesCount) {
- error("wrong resource type %i", _resourceList[j].resourceType);
+ debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", (int)resourceList.size());
+ for (SceneResourceDataArray::iterator j = resourceList.begin(); j != resourceList.end(); ++j) {
+ if (j->resourceType >= typesCount) {
+ error("wrong resource type %i", j->resourceType);
}
- resType = types[_resourceList[j].resourceType];
+ resType = types[j->resourceType];
- debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], _resourceList[j].resourceId);
+ debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], j->resourceId);
}
- free(_resourceList);
}
}
#endif
- debug(3, "LUT has %d entries.", _sceneCount);
+ debug(3, "LUT has %d entries.", _sceneLUT.size());
_sceneLoaded = false;
_sceneNumber = 0;
_chapterNumber = 0;
_sceneResourceId = 0;
_inGame = false;
- _loadDescription = false;
- memset(&_sceneDescription, 0, sizeof(_sceneDescription));
- _resourceListCount = 0;
- _resourceList = NULL;
+ _sceneDescription.reset();
_sceneProc = NULL;
_objectMap = new ObjectMap(_vm);
_actionMap = new ObjectMap(_vm);
- memset(&_bg, 0, sizeof(_bg));
- memset(&_bgMask, 0, sizeof(_bgMask));
}
Scene::~Scene() {
@@ -236,7 +223,6 @@ Scene::~Scene() {
delete _actionMap;
delete _objectMap;
- free(_sceneLUT);
}
void Scene::getResourceTypes(SAGAResourceTypes *&types, int &typesCount) {
@@ -281,7 +267,7 @@ void Scene::startScene() {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
switch (_vm->getGameId()) {
case GID_ITE:
@@ -528,8 +514,7 @@ void Scene::getSlopes(int &beginSlope, int &endSlope) {
}
void Scene::getBGInfo(BGInfo &bgInfo) {
- bgInfo.buffer = _bg.buf;
- bgInfo.bufferLength = _bg.buf_len;
+ bgInfo.buffer = _bg.buffer.getBuffer();
bgInfo.bounds.left = 0;
bgInfo.bounds.top = 0;
@@ -581,15 +566,14 @@ bool Scene::offscreenPath(Point &testPoint) {
}
-void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength) {
+void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer) {
if (!_bgMask.loaded) {
error("Scene::getBGMaskInfo _bgMask not loaded");
}
width = _bgMask.w;
height = _bgMask.h;
- buffer = _bgMask.buf;
- bufferLength = _bgMask.buf_len;
+ buffer = _bgMask.buffer.getBuffer();
}
void Scene::initDoorsState() {
@@ -597,9 +581,8 @@ void Scene::initDoorsState() {
}
void Scene::loadScene(LoadSceneParams &loadSceneParams) {
- size_t i;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
static PalEntry current_pal[PAL_ENTRIES];
if (loadSceneParams.transitionType == kTransitionFade)
@@ -610,7 +593,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kCursorEvent;
event.op = kEventSetBusyCursor;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_chapterPointsChanged = false;
@@ -623,8 +606,8 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
if (loadSceneParams.chapter == 6 || loadSceneParams.chapter == 8)
_vm->_interface->setLeftPortrait(0);
- _vm->_anim->freeCutawayList();
- _vm->_script->freeModules();
+ _vm->_anim->clearCutawayList();
+ _vm->_script->clearModules();
// deleteAllScenes();
@@ -659,8 +642,6 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
error("Scene::loadScene(): Error, a scene is already loaded");
}
- _loadDescription = true;
-
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
if (loadSceneParams.loadFlag == kLoadBySceneNumber) // When will we get rid of it?
@@ -678,16 +659,6 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
_sceneNumber = loadSceneParams.sceneDescriptor;
_sceneResourceId = getSceneResourceId(_sceneNumber);
break;
- case kLoadByDescription:
- _sceneNumber = -1;
- _sceneResourceId = -1;
- assert(loadSceneParams.sceneDescription != NULL);
- assert(loadSceneParams.sceneDescription->resourceList != NULL);
- _loadDescription = false;
- _sceneDescription = *loadSceneParams.sceneDescription;
- _resourceList = loadSceneParams.sceneDescription->resourceList;
- _resourceListCount = loadSceneParams.sceneDescription->resourceListCount;
- break;
}
debug(3, "Loading scene number %d:", _sceneNumber);
@@ -702,34 +673,15 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
}
// Load scene descriptor and resource list resources
- if (_loadDescription) {
- debug(3, "Loading scene resource %i", _sceneResourceId);
-
- loadSceneDescriptor(_sceneResourceId);
-
- loadSceneResourceList(_sceneDescription.resourceListResourceId);
- } else {
- debug(3, "Loading memory scene resource");
- }
+ debug(3, "Loading scene resource %i", _sceneResourceId);
- // Load resources from scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- if (!_resourceList[i].invalid) {
- _vm->_resource->loadResource(_sceneContext, _resourceList[i].resourceId,
- _resourceList[i].buffer, _resourceList[i].size);
+ loadSceneDescriptor(_sceneResourceId);
-
- if (_resourceList[i].size >= 6) {
- if (!memcmp(_resourceList[i].buffer, "DUMMY!", 6)) {
- _resourceList[i].invalid = true;
- warning("DUMMY resource %i", _resourceList[i].resourceId);
- }
- }
- }
- }
+ SceneResourceDataArray resourceList;
+ loadSceneResourceList(_sceneDescription.resourceListResourceId, resourceList);
// Process resources from scene resource list
- processSceneResources();
+ processSceneResources(resourceList);
if (_sceneDescription.flags & kSceneFlagISO) {
_outsetSceneNumber = _sceneNumber;
@@ -748,7 +700,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
_sceneLoaded = true;
- q_event = NULL;
+ eventColumns = NULL;
if (loadSceneParams.transitionType == kTransitionFade) {
@@ -762,7 +714,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = current_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -771,7 +723,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Display scene background, but stay with black palette
event.type = kEvTImmediate;
@@ -780,7 +732,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
@@ -796,7 +748,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param4 = _sceneNumber; // Object
event.param5 = loadSceneParams.actorsEntrance; // With Object
event.param6 = 0; // Actor
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
}
if (loadSceneParams.transitionType == kTransitionFade) {
@@ -808,7 +760,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kFadeIn;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ eventColumns = _vm->_events->chain(eventColumns, event);
// Fade in from black to the scene background palette
event.type = kEvTImmediate;
@@ -817,7 +769,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = _bg.pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -826,7 +778,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
if (loadSceneParams.sceneProc == NULL) {
@@ -845,13 +797,13 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param2 = MUSIC_DEFAULT;
event.op = kEventPlay;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
event.type = kEvTOneshot;
event.code = kMusicEvent;
event.op = kEventStop;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
}
@@ -861,14 +813,14 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.op = kEventDisplay;
event.param = kEvPSetPalette;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Begin palette cycle animation if present
event.type = kEvTOneshot;
event.code = kPalAnimEvent;
event.op = kEventCycleStart;
event.time = 0;
- q_event = _vm->_events->queue(&event);
+ _vm->_events->queue(event);
// Start the scene main script
if (_sceneDescription.sceneScriptEntrypointNumber > 0) {
@@ -882,7 +834,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.param4 = _sceneNumber; // Object
event.param5 = loadSceneParams.actorsEntrance; // With Object
event.param6 = 0; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
debug(3, "Scene started");
@@ -905,7 +857,7 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kInterfaceEvent;
event.op = kEventActivate;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
// Change the cursor back to a crosshair in IHNM
@@ -913,23 +865,22 @@ void Scene::loadScene(LoadSceneParams &loadSceneParams) {
event.code = kCursorEvent;
event.op = kEventSetNormalCursor;
event.time = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Scene::loadSceneDescriptor(uint32 resourceId) {
- byte *sceneDescriptorData;
- size_t sceneDescriptorDataLength;
+ ByteArray sceneDescriptorData;
- memset(&_sceneDescription, 0, sizeof(_sceneDescription));
+ _sceneDescription.reset();
if (resourceId == 0) {
return;
}
- _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData, sceneDescriptorDataLength);
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData);
- if (sceneDescriptorDataLength == 16) {
- MemoryReadStreamEndian readS(sceneDescriptorData, sceneDescriptorDataLength, _sceneContext->isBigEndian());
+ if (sceneDescriptorData.size() == 16) {
+ ByteArrayReadStreamEndian readS(sceneDescriptorData, _sceneContext->isBigEndian());
_sceneDescription.flags = readS.readSint16();
_sceneDescription.resourceListResourceId = readS.readSint16();
@@ -940,53 +891,44 @@ void Scene::loadSceneDescriptor(uint32 resourceId) {
_sceneDescription.startScriptEntrypointNumber = readS.readUint16();
_sceneDescription.musicResourceId = readS.readSint16();
}
-
- free(sceneDescriptorData);
}
-void Scene::loadSceneResourceList(uint32 resourceId) {
- byte *resourceListData;
- size_t resourceListDataLength;
- size_t i;
+void Scene::loadSceneResourceList(uint32 resourceId, SceneResourceDataArray &resourceList) {
+ ByteArray resourceListData;
- _resourceListCount = 0;
- _resourceList = NULL;
+ resourceList.clear();
if (resourceId == 0) {
return;
}
// Load the scene resource table
- _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData, resourceListDataLength);
+ _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData);
- if ((resourceListDataLength % SAGA_RESLIST_ENTRY_LEN) == 0) {
- MemoryReadStreamEndian readS(resourceListData, resourceListDataLength, _sceneContext->isBigEndian());
+ if ((resourceListData.size() % SAGA_RESLIST_ENTRY_LEN) == 0) {
+ ByteArrayReadStreamEndian readS(resourceListData, _sceneContext->isBigEndian());
// Allocate memory for scene resource list
- _resourceListCount = resourceListDataLength / SAGA_RESLIST_ENTRY_LEN;
- debug(3, "Scene resource list contains %i entries", (int)_resourceListCount);
- _resourceList = (SceneResourceData *)calloc(_resourceListCount, sizeof(*_resourceList));
+ resourceList.resize(resourceListData.size() / SAGA_RESLIST_ENTRY_LEN);
+ debug(3, "Scene resource list contains %i entries", (int)resourceList.size());
// Load scene resource list from raw scene
// resource table
debug(3, "Loading scene resource list");
- for (i = 0; i < _resourceListCount; i++) {
- _resourceList[i].resourceId = readS.readUint16();
- _resourceList[i].resourceType = readS.readUint16();
+ for (SceneResourceDataArray::iterator resource = resourceList.begin(); resource != resourceList.end(); ++resource) {
+ resource->resourceId = readS.readUint16();
+ resource->resourceType = readS.readUint16();
// demo version may contain invalid resourceId
- _resourceList[i].invalid = !_sceneContext->validResourceId(_resourceList[i].resourceId);
+ resource->invalid = !_sceneContext->validResourceId(resource->resourceId);
}
}
- free(resourceListData);
}
-void Scene::processSceneResources() {
- byte *resourceData;
- size_t resourceDataLength;
+void Scene::processSceneResources(SceneResourceDataArray &resourceList) {
+ ByteArray resourceData;
const byte *palPointer;
- size_t i;
SAGAResourceTypes *types = 0;
int typesCount = 0;
SAGAResourceTypes resType;
@@ -994,22 +936,33 @@ void Scene::processSceneResources() {
getResourceTypes(types, typesCount);
// Process the scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- if (_resourceList[i].invalid) {
+ for (SceneResourceDataArray::iterator resource = resourceList.begin(); resource != resourceList.end(); ++resource) {
+ if (resource->invalid) {
+ continue;
+ }
+ _vm->_resource->loadResource(_sceneContext, resource->resourceId, resourceData);
+
+
+ if (resourceData.size() >= 6) {
+ if (!memcmp(resourceData.getBuffer(), "DUMMY!", 6)) {
+ resource->invalid = true;
+ warning("DUMMY resource %i", resource->resourceId);
+ }
+ }
+
+ if (resource->invalid) {
continue;
}
- resourceData = _resourceList[i].buffer;
- resourceDataLength = _resourceList[i].size;
- if (_resourceList[i].resourceType >= typesCount) {
- error("Scene::processSceneResources() wrong resource type %i", _resourceList[i].resourceType);
+ if (resource->resourceType >= typesCount) {
+ error("Scene::processSceneResources() wrong resource type %i", resource->resourceType);
}
- resType = types[_resourceList[i].resourceType];
+ resType = types[resource->resourceType];
switch (resType) {
case SAGA_UNKNOWN:
- warning("UNKNOWN resourceType %i", _resourceList[i].resourceType);
+ warning("UNKNOWN resourceType %i", resource->resourceType);
break;
case SAGA_ACTOR:
//for (a = actorsInScene; a; a = a->nextInScene)
@@ -1027,20 +980,16 @@ void Scene::processSceneResources() {
}
debug(3, "Loading background resource.");
- _bg.res_buf = resourceData;
- _bg.res_len = resourceDataLength;
- _bg.loaded = 1;
-
- if (_vm->decodeBGImage(_bg.res_buf,
- _bg.res_len,
- &_bg.buf,
- &_bg.buf_len,
+
+ if (!_vm->decodeBGImage(resourceData,
+ _bg.buffer,
&_bg.w,
- &_bg.h) != SUCCESS) {
- error("Scene::processSceneResources() Error loading background resource %i", _resourceList[i].resourceId);
+ &_bg.h)) {
+ error("Scene::processSceneResources() Error loading background resource %i", resource->resourceId);
}
+ _bg.loaded = true;
- palPointer = _vm->getImagePal(_bg.res_buf, _bg.res_len);
+ palPointer = _vm->getImagePal(resourceData);
memcpy(_bg.pal, palPointer, sizeof(_bg.pal));
break;
case SAGA_BG_MASK: // Scene background mask resource
@@ -1048,30 +997,27 @@ void Scene::processSceneResources() {
error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered");
}
debug(3, "Loading BACKGROUND MASK resource.");
- _bgMask.res_buf = resourceData;
- _bgMask.res_len = resourceDataLength;
- _bgMask.loaded = 1;
- _vm->decodeBGImage(_bgMask.res_buf, _bgMask.res_len, &_bgMask.buf,
- &_bgMask.buf_len, &_bgMask.w, &_bgMask.h, true);
+ _vm->decodeBGImage(resourceData, _bgMask.buffer, &_bgMask.w, &_bgMask.h, true);
+ _bgMask.loaded = true;
// At least in ITE the mask needs to be clipped.
_bgMask.w = MIN(_bgMask.w, _vm->getDisplayInfo().width);
_bgMask.h = MIN(_bgMask.h, getHeight());
- debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, (int)_bgMask.buf_len);
+ debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, _bgMask.buffer.size());
break;
case SAGA_STRINGS:
debug(3, "Loading scene strings resource...");
- _vm->loadStrings(_sceneStrings, resourceData, resourceDataLength);
+ _vm->loadStrings(_sceneStrings, resourceData);
break;
case SAGA_OBJECT_MAP:
debug(3, "Loading object map resource...");
- _objectMap->load(resourceData, resourceDataLength);
+ _objectMap->load(resourceData);
break;
case SAGA_ACTION_MAP:
debug(3, "Loading action map resource...");
- _actionMap->load(resourceData, resourceDataLength);
+ _actionMap->load(resourceData);
break;
case SAGA_ISO_IMAGES:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1080,7 +1026,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric images resource.");
- _vm->_isoMap->loadImages(resourceData, resourceDataLength);
+ _vm->_isoMap->loadImages(resourceData);
break;
case SAGA_ISO_MAP:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1089,7 +1035,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric map resource.");
- _vm->_isoMap->loadMap(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMap(resourceData);
break;
case SAGA_ISO_PLATFORMS:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1098,7 +1044,7 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric platforms resource.");
- _vm->_isoMap->loadPlatforms(resourceData, resourceDataLength);
+ _vm->_isoMap->loadPlatforms(resourceData);
break;
case SAGA_ISO_METATILES:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1107,20 +1053,20 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric metatiles resource.");
- _vm->_isoMap->loadMetaTiles(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMetaTiles(resourceData);
break;
case SAGA_ANIM:
{
- uint16 animId = _resourceList[i].resourceType - 14;
+ uint16 animId = resource->resourceType - 14;
debug(3, "Loading animation resource animId=%i", animId);
- _vm->_anim->load(animId, resourceData, resourceDataLength);
+ _vm->_anim->load(animId, resourceData);
}
break;
case SAGA_ENTRY:
debug(3, "Loading entry list resource...");
- loadSceneEntryList(resourceData, resourceDataLength);
+ loadSceneEntryList(resourceData);
break;
case SAGA_ISO_MULTI:
if (!(_sceneDescription.flags & kSceneFlagISO)) {
@@ -1129,23 +1075,23 @@ void Scene::processSceneResources() {
debug(3, "Loading isometric multi resource.");
- _vm->_isoMap->loadMulti(resourceData, resourceDataLength);
+ _vm->_isoMap->loadMulti(resourceData);
break;
case SAGA_PAL_ANIM:
debug(3, "Loading palette animation resource.");
- _vm->_palanim->loadPalAnim(resourceData, resourceDataLength);
+ _vm->_palanim->loadPalAnim(resourceData);
break;
case SAGA_FACES:
if (_vm->getGameId() == GID_ITE)
- _vm->_interface->loadScenePortraits(_resourceList[i].resourceId);
+ _vm->_interface->loadScenePortraits(resource->resourceId);
break;
case SAGA_PALETTE:
{
PalEntry pal[PAL_ENTRIES];
- byte *palPtr = resourceData;
+ byte *palPtr = resourceData.getBuffer();
- if (resourceDataLength < 3 * PAL_ENTRIES)
- error("Too small scene palette %i", (int)resourceDataLength);
+ if (resourceData.size() < 3 * PAL_ENTRIES)
+ error("Too small scene palette %i", (int)resourceData.size());
for (uint16 c = 0; c < PAL_ENTRIES; c++) {
pal[c].red = *palPtr++;
@@ -1156,7 +1102,7 @@ void Scene::processSceneResources() {
}
break;
default:
- error("Scene::ProcessSceneResources() Encountered unknown resource type %i", _resourceList[i].resourceType);
+ error("Scene::ProcessSceneResources() Encountered unknown resource type %i", resource->resourceType);
break;
}
}
@@ -1184,7 +1130,6 @@ void Scene::draw() {
void Scene::endScene() {
Rect rect;
- size_t i;
if (!_sceneLoaded)
return;
@@ -1222,37 +1167,28 @@ void Scene::endScene() {
// Free scene background
if (_bg.loaded) {
- free(_bg.buf);
- _bg.loaded = 0;
+ _bg.buffer.clear();
+ _bg.loaded = false;
}
// Free scene background mask
if (_bgMask.loaded) {
- free(_bgMask.buf);
- _bgMask.loaded = 0;
- }
-
- // Free scene resource list
- for (i = 0; i < _resourceListCount; i++) {
- free(_resourceList[i].buffer);
- }
-
- if (_loadDescription) {
- free(_resourceList);
+ _bgMask.buffer.clear();
+ _bgMask.loaded = false;
}
// Free animation info list
_vm->_anim->reset();
- _vm->_palanim->freePalAnim();
+ _vm->_palanim->clear();
- _objectMap->freeMem();
- _actionMap->freeMem();
- _entryList.freeMem();
- _sceneStrings.freeMem();
+ _objectMap->clear();
+ _actionMap->clear();
+ _entryList.clear();
+ _sceneStrings.clear();
if (_vm->getGameId() == GID_ITE)
- _vm->_isoMap->freeMem();
+ _vm->_isoMap->clear();
_vm->_events->clearList();
_textList.clear();
@@ -1275,7 +1211,7 @@ void Scene::restoreScene() {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_vm->_gfx->showCursor(true);
}
@@ -1285,7 +1221,7 @@ void Scene::cmdSceneChange(int argc, const char **argv) {
scene_num = atoi(argv[1]);
- if ((scene_num < 1) || (scene_num >= _sceneCount)) {
+ if ((scene_num < 1) || (uint(scene_num) >= _sceneLUT.size())) {
_vm->_console->DebugPrintf("Invalid scene number.\n");
return;
}
@@ -1303,34 +1239,29 @@ void Scene::cmdObjectMapInfo() {
_objectMap->cmdInfo();
}
-void Scene::loadSceneEntryList(const byte* resourcePointer, size_t resourceLength) {
- int i;
-
- _entryList.entryListCount = resourceLength / 8;
-
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _sceneContext->isBigEndian());
+void Scene::loadSceneEntryList(const ByteArray &resourceData) {
+ uint i;
+ if (!_entryList.empty()) {
+ error("Scene::loadSceneEntryList entryList not empty");
+ }
- if (_entryList.entryList)
- error("Scene::loadSceneEntryList entryList != NULL");
+ _entryList.resize(resourceData.size() / 8);
- _entryList.entryList = (SceneEntry *) malloc(_entryList.entryListCount * sizeof(*_entryList.entryList));
- if (_entryList.entryList == NULL) {
- memoryError("Scene::loadSceneEntryList");
- }
+ ByteArrayReadStreamEndian readS(resourceData, _sceneContext->isBigEndian());
- for (i = 0; i < _entryList.entryListCount; i++) {
- _entryList.entryList[i].location.x = readS.readSint16();
- _entryList.entryList[i].location.y = readS.readSint16();
- _entryList.entryList[i].location.z = readS.readSint16();
- _entryList.entryList[i].facing = readS.readUint16();
+ for (i = 0; i < _entryList.size(); i++) {
+ _entryList[i].location.x = readS.readSint16();
+ _entryList[i].location.y = readS.readSint16();
+ _entryList[i].location.z = readS.readSint16();
+ _entryList[i].facing = readS.readUint16();
}
}
void Scene::clearPlacard() {
static PalEntry cur_pal[PAL_ENTRIES];
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
_vm->_interface->setFadeMode(kFadeOut);
@@ -1342,7 +1273,7 @@ void Scene::clearPlacard() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
// set fade mode
event.type = kEvTImmediate;
@@ -1351,14 +1282,14 @@ void Scene::clearPlacard() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
if (_vm->getGameId() == GID_ITE) {
event.type = kEvTOneshot;
event.code = kTextEvent;
event.op = kEventRemove;
event.data = _vm->_script->getPlacardTextEntry();
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
} else {
_vm->_scene->_textList.clear();
}
@@ -1368,7 +1299,7 @@ void Scene::clearPlacard() {
event.op = kEventRestoreMode;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
#ifdef ENABLE_IHNM
if (_vm->getGameId() == GID_IHNM) {
@@ -1379,7 +1310,7 @@ void Scene::clearPlacard() {
event.param = kPanelMain;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
#endif
@@ -1390,7 +1321,7 @@ void Scene::clearPlacard() {
event.param = kEvPNoSetPalette;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1399,7 +1330,7 @@ void Scene::clearPlacard() {
event.param = kFadeIn;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Fade in from black to the scene background palette
event.type = kEvTImmediate;
@@ -1408,7 +1339,7 @@ void Scene::clearPlacard() {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = _bg.pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1417,18 +1348,18 @@ void Scene::clearPlacard() {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventShow;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
#ifdef ENABLE_IHNM
@@ -1439,7 +1370,7 @@ void Scene::showPsychicProfile(const char *text) {
PalEntry *pal;
TextListEntry textEntry;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
if (_vm->_interface->getMode() == kPanelPlacard)
return;
@@ -1453,7 +1384,7 @@ void Scene::showPsychicProfile(const char *text) {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_interface->setFadeMode(kFadeOut);
@@ -1465,7 +1396,7 @@ void Scene::showPsychicProfile(const char *text) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1474,17 +1405,17 @@ void Scene::showPsychicProfile(const char *text) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kInterfaceEvent;
event.op = kEventClearStatus;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Set the background and palette for the psychic profile
event.type = kEvTOneshot;
event.code = kPsychicProfileBgEvent;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
_vm->_scene->_textList.clear();
@@ -1507,7 +1438,7 @@ void Scene::showPsychicProfile(const char *text) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _psychicProfileTextEntry;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
_vm->_scene->getBGPal(pal);
@@ -1518,13 +1449,13 @@ void Scene::showPsychicProfile(const char *text) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
void Scene::clearPsychicProfile() {
diff --git a/engines/saga/scene.h b/engines/saga/scene.h
index 0131e01abb..8d48e71445 100644
--- a/engines/saga/scene.h
+++ b/engines/saga/scene.h
@@ -32,6 +32,7 @@
#include "saga/actor.h"
#include "saga/interface.h"
#include "saga/puzzle.h"
+#include "saga/events.h"
namespace Saga {
@@ -55,8 +56,6 @@ namespace Saga {
class ObjectMap;
-struct Event;
-
enum SceneFlags {
kSceneFlagISO = 1,
kSceneFlagShowCursor = 2
@@ -74,7 +73,6 @@ enum FTA2Endings {
struct BGInfo {
Rect bounds;
byte *buffer;
- size_t bufferLength;
};
typedef int (SceneProc) (int, void *);
@@ -112,11 +110,14 @@ enum SAGAResourceTypes {
struct SceneResourceData {
uint32 resourceId;
int resourceType;
- byte *buffer;
- size_t size;
bool invalid;
+
+ SceneResourceData() : resourceId(0), resourceType(0), invalid(false) {
+ }
};
+typedef Common::Array<SceneResourceData> SceneResourceDataArray;
+
#define SAGA_SCENE_DESC_LEN 16
struct SceneDescription {
@@ -128,47 +129,31 @@ struct SceneDescription {
uint16 sceneScriptEntrypointNumber;
uint16 startScriptEntrypointNumber;
int16 musicResourceId;
- SceneResourceData *resourceList;
- size_t resourceListCount;
+
+ void reset() {
+ flags = resourceListResourceId = endSlope = beginSlope = scriptModuleNumber = sceneScriptEntrypointNumber = startScriptEntrypointNumber = musicResourceId = 0;
+ }
};
struct SceneEntry {
Location location;
- int facing;
+ uint16 facing;
};
-struct SceneEntryList {
- SceneEntry *entryList;
- int entryListCount;
-
- const SceneEntry * getEntry(int index) {
- if ((index < 0) || (index >= entryListCount)) {
- error("SceneEntryList::getEntry wrong index (%d)", index);
- }
- return &entryList[index];
- }
- void freeMem() {
- free(entryList);
- memset(this, 0, sizeof(*this));
- }
- SceneEntryList() {
- memset(this, 0, sizeof(*this));
- }
- ~SceneEntryList() {
- freeMem();
- }
+class SceneEntryList : public Common::Array<SceneEntry> {
};
struct SceneImage {
- int loaded;
+ bool loaded;
int w;
int h;
int p;
- byte *buf;
- size_t buf_len;
- byte *res_buf;
- size_t res_len;
+ ByteArray buffer;
PalEntry pal[256];
+
+ SceneImage() : loaded(false), w(0), h(0), p(0) {
+ memset(pal, 0, sizeof(pal));
+ }
};
@@ -179,14 +164,12 @@ enum SceneTransitionType {
enum SceneLoadFlags {
kLoadByResourceId,
- kLoadBySceneNumber,
- kLoadByDescription
+ kLoadBySceneNumber
};
struct LoadSceneParams {
int32 sceneDescriptor;
SceneLoadFlags loadFlag;
- SceneDescription* sceneDescription;
SceneProc *sceneProc;
bool sceneSkipTarget;
SceneTransitionType transitionType;
@@ -248,8 +231,8 @@ class Scene {
void skipScene();
void endScene();
void restoreScene();
- void queueScene(LoadSceneParams *sceneQueue) {
- _sceneQueue.push_back(*sceneQueue);
+ void queueScene(const LoadSceneParams &sceneQueue) {
+ _sceneQueue.push_back(sceneQueue);
}
void draw();
@@ -258,7 +241,7 @@ class Scene {
bool isInIntro() { return !_inGame; }
const Rect& getSceneClip() const { return _sceneClip; }
- void getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength);
+ void getBGMaskInfo(int &width, int &height, byte *&buffer);
int isBGMaskPresent() { return _bgMask.loaded; }
int getBGMaskType(const Point &testPoint) {
@@ -274,7 +257,7 @@ class Scene {
}
#endif
- return (_bgMask.buf[offset] >> 4) & 0x0f;
+ return (_bgMask.buffer[offset] >> 4) & 0x0f;
}
bool validBGMaskPoint(const Point &testPoint) {
@@ -325,7 +308,7 @@ class Scene {
bool isSceneLoaded() const { return _sceneLoaded; }
- int getSceneResourceId(int sceneNumber) {
+ uint16 getSceneResourceId(int sceneNumber) {
#ifdef SCENE_DEBUG
if ((sceneNumber < 0) || (sceneNumber >= _sceneCount)) {
error("getSceneResourceId: wrong sceneNumber %i", sceneNumber);
@@ -376,17 +359,16 @@ class Scene {
private:
void loadScene(LoadSceneParams &loadSceneParams);
void loadSceneDescriptor(uint32 resourceId);
- void loadSceneResourceList(uint32 resourceId);
- void loadSceneEntryList(const byte* resourcePointer, size_t resourceLength);
- void processSceneResources();
+ void loadSceneResourceList(uint32 resourceId, SceneResourceDataArray &resourceList);
+ void loadSceneEntryList(const ByteArray &resourceData);
+ void processSceneResources(SceneResourceDataArray &resourceList);
void getResourceTypes(SAGAResourceTypes *&types, int &typesCount);
SagaEngine *_vm;
ResourceContext *_sceneContext;
- int *_sceneLUT;
- int _sceneCount;
+ Common::Array<uint16> _sceneLUT;
SceneQueueList _sceneQueue;
bool _sceneLoaded;
int _currentProtag;
@@ -398,10 +380,7 @@ class Scene {
int _currentMusicRepeat;
bool _chapterPointsChanged;
bool _inGame;
- bool _loadDescription;
SceneDescription _sceneDescription;
- size_t _resourceListCount;
- SceneResourceData *_resourceList;
SceneProc *_sceneProc;
SceneImage _bg;
SceneImage _bgMask;
@@ -456,8 +435,8 @@ class Scene {
static int SC_ITEIntroFaireTentProc(int param, void *refCon);
private:
- Event *ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]);
- Event *ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]);
+ EventColumns *ITEQueueDialogue(EventColumns *eventColumns, int n_dialogues, const IntroDialogue dialogue[]);
+ EventColumns *ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]);
int ITEIntroAnimProc(int param);
int ITEIntroCave1Proc(int param);
int ITEIntroCave2Proc(int param);
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
index 5fd120ac33..3ae2f19e1b 100644
--- a/engines/saga/script.cpp
+++ b/engines/saga/script.cpp
@@ -46,12 +46,11 @@ namespace Saga {
SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
ResourceContext *resourceContext;
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
int prevTell;
- int i, j;
- byte *stringsPointer;
- size_t stringsLength;
+ uint ui;
+ int j;
+ ByteArray stringsData;
//initialize member variables
_abortEnabled = true;
@@ -67,9 +66,7 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
_pointerObject = ID_NOTHING;
_staticSize = 0;
- _commonBufferSize = COMMON_BUFFER_SIZE;
- _commonBuffer = (byte*)malloc(_commonBufferSize);
- memset(_commonBuffer, 0, _commonBufferSize);
+ _commonBuffer.resize(COMMON_BUFFER_SIZE);
debug(8, "Initializing scripting subsystem");
// Load script resource file context
@@ -86,47 +83,41 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
uint32 scriptResourceId = 0;
scriptResourceId = _vm->getResourceDescription()->moduleLUTResourceId;
debug(3, "Loading module LUT from resource %i", scriptResourceId);
- _vm->_resource->loadResource(resourceContext, scriptResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, scriptResourceId, resourceData);
// Create logical script LUT from resource
- if (resourceLength % 22 == 0) { // ITE CD
+ if (resourceData.size() % 22 == 0) { // ITE CD
_modulesLUTEntryLen = 22;
- } else if (resourceLength % 16 == 0) { // ITE disk, IHNM
+ } else if (resourceData.size() % 16 == 0) { // ITE disk, IHNM
_modulesLUTEntryLen = 16;
} else {
- error("Script::Script() Invalid script lookup table length (%i)", (int)resourceLength);
+ error("Script::Script() Invalid script lookup table length (%i)", (int)resourceData.size());
}
// Calculate number of entries
- _modulesCount = resourceLength / _modulesLUTEntryLen;
+ int modulesCount = resourceData.size() / _modulesLUTEntryLen;
- debug(3, "LUT has %i entries", _modulesCount);
+ debug(3, "LUT has %i entries", modulesCount);
// Allocate space for logical LUT
- _modules = (ModuleData *)malloc(_modulesCount * sizeof(*_modules));
- if (_modules == NULL) {
- memoryError("Script::Script()");
- }
+ _modules.resize(modulesCount);
// Convert LUT resource to logical LUT
- MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, resourceContext->isBigEndian());
- for (i = 0; i < _modulesCount; i++) {
- memset(&_modules[i], 0, sizeof(ModuleData));
+ ByteArrayReadStreamEndian scriptS(resourceData, resourceContext->isBigEndian());
+ for (ui = 0; ui < _modules.size(); ui++) {
prevTell = scriptS.pos();
- _modules[i].scriptResourceId = scriptS.readUint16();
- _modules[i].stringsResourceId = scriptS.readUint16();
- _modules[i].voicesResourceId = scriptS.readUint16();
+ _modules[ui].scriptResourceId = scriptS.readUint16();
+ _modules[ui].stringsResourceId = scriptS.readUint16();
+ _modules[ui].voicesResourceId = scriptS.readUint16();
// Skip the unused portion of the structure
for (j = scriptS.pos(); j < prevTell + _modulesLUTEntryLen; j++) {
if (scriptS.readByte() != 0)
- warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
+ warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", ui, j);
}
}
- free(resourcePointer);
-
// TODO
//
// In ITE, the "main strings" resource contains both the verb strings
@@ -135,10 +126,9 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
// In IHNM, the "main strings" contains the verb strings, but not the
// object names. At least, I think that's the case.
- _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->mainStringsResourceId, stringsPointer, stringsLength);
+ _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->mainStringsResourceId, stringsData);
- _vm->loadStrings(_mainStrings, stringsPointer, stringsLength);
- free(stringsPointer);
+ _vm->loadStrings(_mainStrings, stringsData);
setupScriptOpcodeList();
@@ -157,19 +147,10 @@ SAGA1Script::SAGA1Script(SagaEngine *vm) : Script(vm) {
SAGA1Script::~SAGA1Script() {
debug(8, "Shutting down scripting subsystem.");
-
- _mainStrings.freeMem();
- _globalVoiceLUT.freeMem();
-
- freeModules();
- free(_modules);
-
- free(_commonBuffer);
}
SAGA2Script::SAGA2Script(SagaEngine *vm) : Script(vm) {
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
debug(8, "Initializing scripting subsystem");
// Load script resource file context
@@ -184,14 +165,14 @@ SAGA2Script::SAGA2Script(SagaEngine *vm) : Script(vm) {
if (entryNum < 0)
error("Unable to locate the script's export segment");
debug(3, "Loading module LUT from resource %i", entryNum);
- _vm->_resource->loadResource(_scriptContext, (uint32)entryNum, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, (uint32)entryNum, resourceData);
_modulesLUTEntryLen = sizeof(uint32);
// Calculate number of entries
- _modulesCount = resourceLength / _modulesLUTEntryLen + 1;
+ int modulesCount = resourceData.size() / _modulesLUTEntryLen + 1;
- debug(3, "LUT has %i entries", _modulesCount);
+ debug(3, "LUT has %i entries", modulesCount);
// Script data segment
/*
@@ -994,8 +975,8 @@ void Script::opSpeak(SCRIPTOP_PARAMS) {
}
} else {
#endif
- if (thread->_voiceLUT->voicesCount > first)
- sampleResourceId = thread->_voiceLUT->voices[first];
+ if (thread->_voiceLUT->size() > uint16(first))
+ sampleResourceId = (*thread->_voiceLUT)[uint16(first)];
#if 0
}
#endif
@@ -1067,12 +1048,11 @@ void Script::opJmpSeedRandom(SCRIPTOP_PARAMS) {
warning("opJmpSeedRandom");
}
-void Script::loadModule(int scriptModuleNumber) {
- byte *resourcePointer;
- size_t resourceLength;
+void Script::loadModule(uint scriptModuleNumber) {
+ ByteArray resourceData;
// Validate script number
- if ((scriptModuleNumber < 0) || (scriptModuleNumber >= _modulesCount)) {
+ if (scriptModuleNumber >= _modules.size()) {
error("Script::loadScript() Invalid script module number");
}
@@ -1083,79 +1063,70 @@ void Script::loadModule(int scriptModuleNumber) {
// Initialize script data structure
debug(3, "Loading script module #%d", scriptModuleNumber);
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, resourceData);
- loadModuleBase(_modules[scriptModuleNumber], resourcePointer, resourceLength);
- free(resourcePointer);
+ loadModuleBase(_modules[scriptModuleNumber], resourceData);
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, resourceData);
- _vm->loadStrings(_modules[scriptModuleNumber].strings, resourcePointer, resourceLength);
- free(resourcePointer);
+ _vm->loadStrings(_modules[scriptModuleNumber].strings, resourceData);
if (_modules[scriptModuleNumber].voicesResourceId > 0) {
- _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, resourcePointer, resourceLength);
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, resourceData);
- loadVoiceLUT(_modules[scriptModuleNumber].voiceLUT, resourcePointer, resourceLength);
- free(resourcePointer);
+ loadVoiceLUT(_modules[scriptModuleNumber].voiceLUT, resourceData);
}
_modules[scriptModuleNumber].staticOffset = _staticSize;
_staticSize += _modules[scriptModuleNumber].staticSize;
- if (_staticSize > _commonBufferSize) {
- error("Script::loadModule() _staticSize > _commonBufferSize");
+ if (_staticSize > _commonBuffer.size()) {
+ error("Script::loadModule() _staticSize > _commonBuffer.size()");
}
_modules[scriptModuleNumber].loaded = true;
}
-void Script::freeModules() {
- int i;
- for (i = 0; i < _modulesCount; i++) {
+void Script::clearModules() {
+ uint i;
+ for (i = 0; i < _modules.size(); i++) {
if (_modules[i].loaded) {
- _modules[i].freeMem();
- _modules[i].loaded = false;
+ _modules[i].clear();
}
}
_staticSize = 0;
}
-void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength) {
- int i;
+void Script::loadModuleBase(ModuleData &module, const ByteArray &resourceData) {
+ uint i;
debug(3, "Loading module base...");
- module.moduleBase = (byte*)malloc(resourceLength);
- module.moduleBaseSize = resourceLength;
+ module.moduleBase.assign(resourceData);
- memcpy(module.moduleBase, resourcePointer, resourceLength);
+ ByteArrayReadStreamEndian scriptS(module.moduleBase, _scriptContext->isBigEndian());
- MemoryReadStreamEndian scriptS(module.moduleBase, module.moduleBaseSize, _scriptContext->isBigEndian());
-
- module.entryPointsCount = scriptS.readUint16();
+ uint entryPointsCount = scriptS.readUint16();
scriptS.readUint16(); //skip
- module.entryPointsTableOffset = scriptS.readUint16();
+ uint16 entryPointsTableOffset; // offset of entrypoint table in moduleBase
+ entryPointsTableOffset = scriptS.readUint16();
scriptS.readUint16(); //skip
- if ((module.moduleBaseSize - module.entryPointsTableOffset) < (module.entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
+ if ((module.moduleBase.size() - entryPointsTableOffset) < (entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
error("Script::loadModuleBase() Invalid table offset");
}
- if (module.entryPointsCount > SCRIPT_MAX) {
+ if (entryPointsCount > SCRIPT_MAX) {
error("Script::loadModuleBase()Script limit exceeded");
}
- module.entryPoints = (EntryPoint *)malloc(module.entryPointsCount * sizeof(*module.entryPoints));
- if (module.entryPoints == NULL) {
- memoryError("Script::loadModuleBase");
- }
+ module.entryPoints.resize(entryPointsCount);
// Read in the entrypoint table
module.staticSize = scriptS.readUint16();
- while (scriptS.pos() < module.entryPointsTableOffset)
+ while (scriptS.pos() < entryPointsTableOffset)
scriptS.readByte();
- for (i = 0; i < module.entryPointsCount; i++) {
+ for (i = 0; i < module.entryPoints.size(); i++) {
// First uint16 is the offset of the entrypoint name from the start
// of the bytecode resource, second uint16 is the offset of the
// bytecode itself for said entrypoint
@@ -1163,26 +1134,21 @@ void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, siz
module.entryPoints[i].offset = scriptS.readUint16();
// Perform a simple range check on offset values
- if ((module.entryPoints[i].nameOffset >= module.moduleBaseSize) || (module.entryPoints[i].offset >= module.moduleBaseSize)) {
+ if ((module.entryPoints[i].nameOffset >= module.moduleBase.size()) || (module.entryPoints[i].offset >= module.moduleBase.size())) {
error("Script::loadModuleBase() Invalid offset encountered in script entrypoint table");
}
}
}
-void Script::loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength) {
+void Script::loadVoiceLUT(VoiceLUT &voiceLUT, const ByteArray &resourceData) {
uint16 i;
- voiceLUT.voicesCount = resourceLength / 2;
+ voiceLUT.resize(resourceData.size() / 2);
- voiceLUT.voices = (uint16 *)malloc(voiceLUT.voicesCount * sizeof(*voiceLUT.voices));
- if (voiceLUT.voices == NULL) {
- error("Script::loadVoiceLUT() not enough memory");
- }
+ ByteArrayReadStreamEndian scriptS(resourceData, _scriptContext->isBigEndian());
- MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, _scriptContext->isBigEndian());
-
- for (i = 0; i < voiceLUT.voicesCount; i++) {
- voiceLUT.voices[i] = scriptS.readUint16();
+ for (i = 0; i < voiceLUT.size(); i++) {
+ voiceLUT[i] = scriptS.readUint16();
}
}
@@ -1315,7 +1281,7 @@ void Script::setVerb(int verb) {
// engine did it, but it appears to work.
_pointerObject = ID_NOTHING;
- setLeftButtonVerb( verb );
+ setLeftButtonVerb(verb);
showVerb();
}
@@ -1442,7 +1408,7 @@ void Script::doVerb() {
event.param4 = _pendingObject[0]; // Object
event.param5 = _pendingObject[1]; // With Object
event.param6 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
} else {
// Show excuse text in ITE CD Versions
@@ -1549,7 +1515,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 +1584,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/script.h b/engines/saga/script.h
index 21afeb5c44..e5054d5f4e 100644
--- a/engines/saga/script.h
+++ b/engines/saga/script.h
@@ -129,16 +129,7 @@ struct EntryPoint {
uint16 offset;
};
-struct VoiceLUT {
- uint16 voicesCount;
- uint16 *voices;
- void freeMem() {
- voicesCount = 0;
- free(voices);
- }
- VoiceLUT() {
- memset(this, 0, sizeof(*this));
- }
+class VoiceLUT : public Common::Array<uint16> {
};
struct ModuleData {
@@ -147,28 +138,29 @@ struct ModuleData {
int stringsResourceId;
int voicesResourceId;
- byte *moduleBase; // all base module
- uint16 moduleBaseSize; // base module size
+ ByteArray moduleBase; // all base module
uint16 staticSize; // size of static data
uint staticOffset; // offset of static data begining in _commonBuffer
-
- uint16 entryPointsTableOffset; // offset of entrypoint table in moduleBase
- uint16 entryPointsCount;
- EntryPoint *entryPoints;
+ Common::Array<EntryPoint> entryPoints;
StringsTable strings;
VoiceLUT voiceLUT;
- void freeMem() {
- strings.freeMem();
- voiceLUT.freeMem();
- free(moduleBase);
- free(entryPoints);
+
+ void clear() {
+ loaded = false;
+ strings.clear();
+ voiceLUT.clear();
+ moduleBase.clear();
+ entryPoints.clear();
+ }
+
+ ModuleData() : loaded(false), scriptResourceId(0), stringsResourceId(0), voicesResourceId(0), staticSize(0), staticOffset(0) {
}
};
class ScriptThread {
public:
- int16 *_stackBuf;
+ Common::Array<int16> _stackBuf;
uint16 _stackTopIndex;
uint16 _frameIndex;
@@ -264,41 +256,15 @@ public:
}
ScriptThread() {
- memset(this, 0xFE, sizeof(*this));
- _flags = kTFlagNone;
- _stackBuf = 0;
- }
-
- // copy constructor
- ScriptThread(const ScriptThread& s) {
- // Verify that s doesn't have a non-zero _stackBuf, for else
- // we would have to clone that buffer, too, which we currently
- // don't do. This case should never occur anyway, though (at
- // least as long as the thread handling code does not change).
- assert(!s._stackBuf);
+ memset(&_frameIndex, 0xFE, sizeof(_frameIndex));
+ memset(_threadVars, 0xFE, sizeof(_threadVars));
+ memset(&_waitType, 0xFE, sizeof(_waitType));
+ memset(&_sleepTime, 0xFE, sizeof(_sleepTime));
+ memset(&_threadObj, 0xFE, sizeof(_threadObj));
+ memset(&_returnValue, 0xFE, sizeof(_threadObj));
+ memset(&_frameWait, 0xFE, sizeof(_frameWait));
- memcpy(this, &s, sizeof(*this));
- }
-
- // assignment operator
- ScriptThread& operator=(const ScriptThread &s) {
- if (this == &s)
- return *this;
-
- // Verify that s doesn't have a non-zero _stackBuf, for else
- // we would have to clone that buffer, too, which we currently
- // don't do. This case should never occur anyway, though (at
- // least as long as the thread handling code does not change).
- assert(!s._stackBuf);
-
- free(_stackBuf);
- memcpy(this, &s, sizeof(*this));
-
- return *this;
- }
-
- ~ScriptThread() {
- free(_stackBuf);
+ _flags = kTFlagNone;
}
};
@@ -315,8 +281,8 @@ public:
Script(SagaEngine *vm);
virtual ~Script();
- void loadModule(int scriptModuleNumber);
- void freeModules();
+ void loadModule(uint scriptModuleNumber);
+ void clearModules();
void doVerb();
void showVerb(int statusColor = -1);
@@ -384,13 +350,11 @@ protected:
ResourceContext *_dataContext;
uint16 _modulesLUTEntryLen;
- ModuleData *_modules;
- int _modulesCount;
+ Common::Array<ModuleData> _modules;
TextListEntry *_placardTextEntry;
friend class SagaEngine;
- byte *_commonBuffer;
- uint _commonBufferSize;
+ ByteArray _commonBuffer;
uint _staticSize;
ScriptThreadList _threadList;
@@ -428,10 +392,10 @@ public:
void wakeUpThreads(int waitType);
void wakeUpThreadsDelayed(int waitType, int sleepTime);
- void loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength);
+ void loadVoiceLUT(VoiceLUT &voiceLUT, const ByteArray &resourceData);
protected:
- void loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength);
+ void loadModuleBase(ModuleData &module, const ByteArray &resourceData);
// runThread returns true if we should break running of other threads
bool runThread(ScriptThread &thread);
diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp
index 328d4040af..1e34362dc4 100644
--- a/engines/saga/sfuncs.cpp
+++ b/engines/saga/sfuncs.cpp
@@ -327,7 +327,7 @@ void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) {
event.param4 = theObject; // Object
event.param5 = withObject; // With Object
event.param6 = objectId;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
// Script function #8 (0x08) nonblocking
@@ -782,11 +782,11 @@ void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) {
for (i = 0; i < actorsCount; i++)
actorsIds[i] = thread->pop();
- if (thread->_voiceLUT->voices) {
+ if (!thread->_voiceLUT->empty()) {
if (_vm->getGameId() == GID_IHNM && stringId >= 338) {
sampleResourceId = -1;
} else {
- sampleResourceId = thread->_voiceLUT->voices[stringId];
+ sampleResourceId = (*thread->_voiceLUT)[stringId];
if (sampleResourceId <= 0 || sampleResourceId > 4000)
sampleResourceId = -1;
}
@@ -953,7 +953,7 @@ void Script::sfPlaceActor(SCRIPTFUNC_PARAMS) {
int frameOffset = thread->pop();
ActorFrameRange *frameRange;
- debug(1, "sfPlaceActor(id = 0x%x, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x,
+ debug(1, "sfPlaceActor(id = 0x%X, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actor->_location.x,
actor->_location.y, actor->_facingDirection, frameType, frameOffset);
if (frameType >= 0) {
@@ -1042,8 +1042,8 @@ void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) {
for (i = 0; i < actorsCount; i++)
actorsIds[i] = thread->pop();
- if (thread->_voiceLUT->voices) {
- sampleResourceId = thread->_voiceLUT->voices[stringId];
+ if (!thread->_voiceLUT->empty()) {
+ sampleResourceId = (*thread->_voiceLUT)[stringId];
if (sampleResourceId <= 0 || sampleResourceId > 4000)
sampleResourceId = -1;
}
@@ -1060,7 +1060,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
static PalEntry cur_pal[PAL_ENTRIES];
PalEntry *pal;
Event event;
- Event *q_event;
+ EventColumns *eventColumns;
thread->wait(kWaitTypePlacard);
@@ -1070,7 +1070,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
- q_event = _vm->_events->queue(&event);
+ eventColumns = _vm->_events->queue(event);
_vm->_interface->setFadeMode(kFadeOut);
@@ -1082,7 +1082,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = cur_pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// set fade mode
event.type = kEvTImmediate;
@@ -1091,12 +1091,12 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.param = kNoFade;
event.time = 0;
event.duration = 0;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kInterfaceEvent;
event.op = kEventClearStatus;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kGraphicsEvent;
@@ -1106,7 +1106,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.param3 = _vm->_scene->getHeight();
event.param4 = 0;
event.param5 = _vm->getDisplayInfo().width;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
// Put the text in the center of the viewport, assuming it will fit on
// one line. If we cannot make that assumption we'll need to extend
@@ -1130,7 +1130,7 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _placardTextEntry;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
_vm->_scene->getBGPal(pal);
event.type = kEvTImmediate;
@@ -1139,13 +1139,13 @@ void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
event.time = 0;
event.duration = kNormalFadeDuration;
event.data = pal;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
event.type = kEvTOneshot;
event.code = kScriptEvent;
event.op = kEventThreadWake;
event.param = kWaitTypePlacard;
- q_event = _vm->_events->chain(q_event, &event);
+ _vm->_events->chain(eventColumns, event);
}
@@ -1349,8 +1349,8 @@ void Script::sfPlayMusic(SCRIPTFUNC_PARAMS) {
return;
}
- if (param1 >= _vm->_music->_songTableLen) {
- warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTableLen - 1);
+ if (uint(param1) >= _vm->_music->_songTable.size()) {
+ warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1);
} else {
_vm->_music->setVolume(_vm->_musicVolume, 1);
_vm->_music->play(_vm->_music->_songTable[param1], param2 ? MUSIC_LOOP : MUSIC_NORMAL);
@@ -1440,7 +1440,7 @@ void Script::sfPlaySound(SCRIPTFUNC_PARAMS) {
int16 param = thread->pop();
int res;
- if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ if (uint(param) < _vm->_sndRes->_fxTable.size()) {
res = _vm->_sndRes->_fxTable[param].res;
if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
res -= 14;
@@ -1455,7 +1455,7 @@ void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
int16 param = thread->pop();
int res;
- if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ if (uint(param) < _vm->_sndRes->_fxTable.size()) {
res = _vm->_sndRes->_fxTable[param].res;
if (_vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
res -= 14;
@@ -1527,7 +1527,7 @@ void Script::finishDialog(int strID, int replyID, int flags, int bitOffset) {
const char *str = _conversingThread->_strings->getString(strID);
if (*str != '[') {
int sampleResourceId = -1;
- sampleResourceId = _conversingThread->_voiceLUT->voices[strID];
+ sampleResourceId = (*_conversingThread->_voiceLUT)[strID];
if (sampleResourceId < 0 || sampleResourceId > 4000)
sampleResourceId = -1;
diff --git a/engines/saga/sfuncs_ihnm.cpp b/engines/saga/sfuncs_ihnm.cpp
index b98c1cb852..dd6bbbe6f8 100644
--- a/engines/saga/sfuncs_ihnm.cpp
+++ b/engines/saga/sfuncs_ihnm.cpp
@@ -247,7 +247,7 @@ void Script::sfScriptFade(SCRIPTFUNC_PARAMS) {
event.param2 = endingBrightness;
event.param3 = firstPalEntry;
event.param4 = lastPalEntry - firstPalEntry + 1;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
}
void Script::sfScriptStartVideo(SCRIPTFUNC_PARAMS) {
@@ -294,7 +294,7 @@ void Script::sfAddIHNMDemoHelpTextLine(SCRIPTFUNC_PARAMS) {
event.code = kTextEvent;
event.op = kEventDisplay;
event.data = _psychicProfileTextEntry;
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
_ihnmDemoCurrentY += _vm->_font->getHeight(kKnownFontVerb, thread->_strings->getString(stringId), 226, kFontCentered);
}
@@ -413,8 +413,8 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) {
return;
}
- if (param1 >= _vm->_music->_songTableLen) {
- warning("sfQueueMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTableLen - 1);
+ if (uint(param1) >= _vm->_music->_songTable.size()) {
+ warning("sfQueueMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1);
} else {
_vm->_music->setVolume(_vm->_musicVolume, 1);
event.type = kEvTOneshot;
@@ -424,7 +424,7 @@ void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) {
event.op = kEventPlay;
event.time = _vm->ticksToMSec(1000);
- _vm->_events->queue(&event);
+ _vm->_events->queue(event);
if (!_vm->_scene->haveChapterPointsChanged()) {
_vm->_scene->setCurrentMusicTrack(param1);
diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp
index 2137423a5a..592c2d0618 100644
--- a/engines/saga/shorten.cpp
+++ b/engines/saga/shorten.cpp
@@ -239,7 +239,7 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
return NULL;
}
- // Get block size
+ // Get block size
if (version > 0) {
blockSize = gReader->getUint32((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2));
maxLPC = gReader->getUint32(2);
@@ -366,7 +366,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
if (maxLPC < lpcNum) {
warning("Safeguard: maxLPC < lpcNum (should never happen)");
maxLPC = lpcNum;
- lpc = (int32 *) realloc(lpc, maxLPC * 4);
+ int32 *tmp = (int32 *) realloc(lpc, maxLPC * 4);
+ if ((tmp != NULL) || (maxLPC == 0)) {
+ lpc = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
}
for (i = 0; i < lpcNum; i++)
@@ -430,7 +435,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
prevSize = size;
size += (blockSize * dataSize);
- unpackedBuffer = (byte *) realloc(unpackedBuffer, size);
+ byte *tmp = (byte *) realloc(unpackedBuffer, size);
+ if ((tmp != NULL) || (size == 0)) {
+ unpackedBuffer = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
pBuf = unpackedBuffer + prevSize;
if (flags & Audio::FLAG_16BITS) {
@@ -464,7 +474,12 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by
uint32 vLen = (uint32)gReader->getURice(5);
prevSize = size;
size += vLen;
- unpackedBuffer = (byte *) realloc(unpackedBuffer, size);
+ byte *tmp = (byte *) realloc(unpackedBuffer, size);
+ if ((tmp != NULL) || (size == 0)) {
+ unpackedBuffer = tmp;
+ } else {
+ error("loadShortenFromStream(): Error while reallocating memory");
+ }
pBuf = unpackedBuffer + prevSize;
while (vLen--) {
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
index a27608dcf5..74fde3e497 100644
--- a/engines/saga/sndres.cpp
+++ b/engines/saga/sndres.cpp
@@ -60,8 +60,11 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
setVoiceBank(0);
if (_vm->getGameId() == GID_ITE) {
- _fxTable = ITE_SfxTable;
- _fxTableLen = ITE_SFXCOUNT;
+ _fxTable.resize(ITE_SFXCOUNT);
+ for (uint i = 0; i < _fxTable.size(); i++) {
+ _fxTable[i].res = ITE_SfxTable[i].res;
+ _fxTable[i].vol = ITE_SfxTable[i].vol;
+ }
#ifdef ENABLE_IHNM
} else if (_vm->getGameId() == GID_IHNM) {
ResourceContext *resourceContext;
@@ -71,32 +74,24 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
error("Resource::loadGlobalResources() resource context not found");
}
- byte *resourcePointer;
- size_t resourceLength;
+ ByteArray resourceData;
if (_vm->isIHNMDemo()) {
- _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_SFX_LUT,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNMDEMO_SFX_LUT, resourceData);
} else {
- _vm->_resource->loadResource(resourceContext, RID_IHNM_SFX_LUT,
- resourcePointer, resourceLength);
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_SFX_LUT, resourceData);
}
- if (resourceLength == 0) {
+ if (resourceData.empty()) {
error("Sndres::SndRes can't read SfxIDs table");
}
- _fxTableIDsLen = resourceLength / 2;
- _fxTableIDs = (int16 *)malloc(_fxTableIDsLen * sizeof(int16));
+ _fxTableIDs.resize(resourceData.size() / 2);
- MemoryReadStream metaS(resourcePointer, resourceLength);
- for (int i = 0; i < _fxTableIDsLen; i++)
+ ByteArrayReadStreamEndian metaS(resourceData);
+ for (uint i = 0; i < _fxTableIDs.size(); i++) {
_fxTableIDs[i] = metaS.readSint16LE();
-
- free(resourcePointer);
-
- _fxTable = 0;
- _fxTableLen = 0;
+ }
#endif
#ifdef ENABLE_SAGA2
} else if (_vm->getGameId() == GID_DINO) {
@@ -108,12 +103,6 @@ SndRes::SndRes(SagaEngine *vm) : _vm(vm), _sfxContext(NULL), _voiceContext(NULL)
}
SndRes::~SndRes() {
-#ifdef ENABLE_IHNM
- if (_vm->getGameId() == GID_IHNM) {
- free(_fxTable);
- free(_fxTableIDs);
- }
-#endif
}
void SndRes::setVoiceBank(int serial) {
@@ -225,6 +214,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 +232,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 +266,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;
}
}
@@ -327,21 +316,25 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
#endif
} else if (resourceType == kSoundVOC) {
data = Audio::loadVOCFromStream(readS, size, rate);
- result = (data != 0);
+ result = (data != NULL);
if (onlyHeader)
free(data);
buffer.flags |= Audio::FLAG_UNSIGNED;
+ buffer.flags &= ~Audio::FLAG_16BITS;
+ buffer.flags &= ~Audio::FLAG_STEREO;
}
if (result) {
buffer.frequency = rate;
buffer.size = size;
- if (!onlyHeader && resourceType != kSoundVOC) {
- buffer.buffer = (byte *)malloc(size);
- readS.read(buffer.buffer, size);
- } else if (!onlyHeader && resourceType == kSoundVOC) {
- buffer.buffer = data;
+ if (!onlyHeader) {
+ if (resourceType == kSoundVOC) {
+ buffer.buffer = data;
+ } else {
+ buffer.buffer = (byte *)malloc(size);
+ readS.read(buffer.buffer, size);
+ }
}
}
break;
diff --git a/engines/saga/sndres.h b/engines/saga/sndres.h
index d5507ebc55..e4bae1b143 100644
--- a/engines/saga/sndres.h
+++ b/engines/saga/sndres.h
@@ -33,6 +33,11 @@
namespace Saga {
+struct FxTable {
+ int16 res;
+ int16 vol;
+};
+
class SndRes {
public:
@@ -44,11 +49,9 @@ public:
int getVoiceLength(uint32 resourceId);
void setVoiceBank(int serial);
- FxTable *_fxTable;
- int _fxTableLen;
+ Common::Array<FxTable> _fxTable;
- int16 *_fxTableIDs;
- int _fxTableIDsLen;
+ Common::Array<int16> _fxTableIDs;
private:
bool load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader);
diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp
index db979e8104..b3fcf4def3 100644
--- a/engines/saga/sound.cpp
+++ b/engines/saga/sound.cpp
@@ -36,7 +36,7 @@
namespace Saga {
Sound::Sound(SagaEngine *vm, Audio::Mixer *mixer) :
- _vm(vm), _mixer(mixer), _voxStream(0) {
+ _vm(vm), _mixer(mixer) {
for (int i = 0; i < SOUND_HANDLES; i++)
_handles[i].type = kFreeHandle;
@@ -45,7 +45,6 @@ Sound::Sound(SagaEngine *vm, Audio::Mixer *mixer) :
}
Sound::~Sound() {
- delete _voxStream;
}
SndHandle *Sound::getHandle() {
diff --git a/engines/saga/sound.h b/engines/saga/sound.h
index 7ee2765a0f..b5f82369f4 100644
--- a/engines/saga/sound.h
+++ b/engines/saga/sound.h
@@ -95,7 +95,6 @@ public:
SagaEngine *_vm;
Audio::Mixer *_mixer;
- MemoryReadStream *_voxStream;
SndHandle _handles[SOUND_HANDLES];
};
diff --git a/engines/saga/sprite.cpp b/engines/saga/sprite.cpp
index c1a9846b47..eb62fb20ff 100644
--- a/engines/saga/sprite.cpp
+++ b/engines/saga/sprite.cpp
@@ -51,13 +51,6 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
error("Sprite::Sprite resource context not found");
}
- _decodeBufLen = DECODE_BUF_LEN;
-
- _decodeBuf = (byte *)malloc(_decodeBufLen);
- if (_decodeBuf == NULL) {
- memoryError("Sprite::Sprite");
- }
-
if (_vm->getGameId() == GID_ITE) {
loadList(_vm->getResourceDescription()->mainSpritesResourceId, _mainSprites);
_arrowSprites = _saveReminderSprites = _inventorySprites = _mainSprites;
@@ -78,67 +71,54 @@ Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
Sprite::~Sprite() {
debug(8, "Shutting down sprite subsystem...");
- _mainSprites.freeMem();
- if (_vm->getGameId() == GID_IHNM) {
- _inventorySprites.freeMem();
- _arrowSprites.freeMem();
- _saveReminderSprites.freeMem();
- }
- free(_decodeBuf);
}
void Sprite::loadList(int resourceId, SpriteList &spriteList) {
SpriteInfo *spriteInfo;
- byte *spriteListData = 0;
- size_t spriteListLength = 0;
+ ByteArray spriteListData;
uint16 oldSpriteCount;
uint16 newSpriteCount;
uint16 spriteCount;
- int i;
+ uint i;
int outputLength, inputLength;
uint32 offset;
const byte *spritePointer;
const byte *spriteDataPointer;
- _vm->_resource->loadResource(_spriteContext, resourceId, spriteListData, spriteListLength);
+ _vm->_resource->loadResource(_spriteContext, resourceId, spriteListData);
- if (spriteListLength == 0) {
+ if (spriteListData.empty()) {
return;
}
- MemoryReadStreamEndian readS(spriteListData, spriteListLength, _spriteContext->isBigEndian());
+ ByteArrayReadStreamEndian readS(spriteListData, _spriteContext->isBigEndian());
spriteCount = readS.readUint16();
debug(9, "Sprites: %d", spriteCount);
- oldSpriteCount = spriteList.spriteCount;
- newSpriteCount = spriteList.spriteCount + spriteCount;
+ oldSpriteCount = spriteList.size();
+ newSpriteCount = oldSpriteCount + spriteCount;
- spriteList.infoList = (SpriteInfo *)realloc(spriteList.infoList, newSpriteCount * sizeof(*spriteList.infoList));
- if (spriteList.infoList == NULL) {
- memoryError("Sprite::loadList");
- }
-
- spriteList.spriteCount = newSpriteCount;
+ spriteList.resize(newSpriteCount);
bool bigHeader = _vm->getGameId() == GID_IHNM || _vm->isMacResources();
- for (i = oldSpriteCount; i < spriteList.spriteCount; i++) {
- spriteInfo = &spriteList.infoList[i];
+ for (i = oldSpriteCount; i < spriteList.size(); i++) {
+ spriteInfo = &spriteList[i];
if (bigHeader)
offset = readS.readUint32();
else
offset = readS.readUint16();
- if (offset >= spriteListLength) {
+ if (offset >= spriteListData.size()) {
// ITE Mac demos throw this warning
warning("Sprite::loadList offset exceeded");
- spriteList.spriteCount = i;
+ spriteList.resize(i);
return;
}
- spritePointer = spriteListData;
+ spritePointer = spriteListData.getBuffer();
spritePointer += offset;
if (bigHeader) {
@@ -163,114 +143,147 @@ void Sprite::loadList(int resourceId, SpriteList &spriteList) {
}
outputLength = spriteInfo->width * spriteInfo->height;
- inputLength = spriteListLength - (spriteDataPointer - spriteListData);
- decodeRLEBuffer(spriteDataPointer, inputLength, outputLength);
- spriteInfo->decodedBuffer = (byte *) malloc(outputLength);
- if (spriteInfo->decodedBuffer == NULL) {
- memoryError("Sprite::loadList");
- }
-
+ inputLength = spriteListData.size() - (spriteDataPointer - spriteListData.getBuffer());
+ spriteInfo->decodedBuffer.resize(outputLength);
+ if (outputLength > 0) {
+ decodeRLEBuffer(spriteDataPointer, inputLength, outputLength);
+ byte *dst = &spriteInfo->decodedBuffer.front();
#ifdef ENABLE_IHNM
- // IHNM sprites are upside-down, for reasons which i can only
- // assume are perverse. To simplify things, flip them now. Not
- // at drawing time.
-
- if (_vm->getGameId() == GID_IHNM) {
- byte *src = _decodeBuf + spriteInfo->width * (spriteInfo->height - 1);
- byte *dst = spriteInfo->decodedBuffer;
-
- for (int j = 0; j < spriteInfo->height; j++) {
- memcpy(dst, src, spriteInfo->width);
- src -= spriteInfo->width;
- dst += spriteInfo->width;
- }
- } else
+ // IHNM sprites are upside-down, for reasons which i can only
+ // assume are perverse. To simplify things, flip them now. Not
+ // at drawing time.
+
+ if (_vm->getGameId() == GID_IHNM) {
+ byte *src = &_decodeBuf[spriteInfo->width * (spriteInfo->height - 1)];
+
+ for (int j = 0; j < spriteInfo->height; j++) {
+ memcpy(dst, src, spriteInfo->width);
+ src -= spriteInfo->width;
+ dst += spriteInfo->width;
+ }
+ } else
#endif
- memcpy(spriteInfo->decodedBuffer, _decodeBuf, outputLength);
+ memcpy(dst, &_decodeBuf.front(), outputLength);
+ }
}
-
- free(spriteListData);
}
-void Sprite::getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer) {
+void Sprite::getScaledSpriteBuffer(SpriteList &spriteList, uint spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer) {
SpriteInfo *spriteInfo;
- if (spriteList.spriteCount <= spriteNumber) {
+ if (spriteList.size() <= spriteNumber) {
// this can occur in IHNM while loading a saved game from chapter 1-5 when being in the end chapter
- warning("spriteList.spriteCount <= spriteNumber");
+ warning("spriteList.size() <= spriteNumber");
return;
}
- spriteInfo = &spriteList.infoList[spriteNumber];
+ spriteInfo = &spriteList[spriteNumber];
if (scale < 256) {
- xAlign = (spriteInfo->xAlign * scale) >> 8;
- yAlign = (spriteInfo->yAlign * scale) >> 8;
+ xAlign = (spriteInfo->xAlign * scale) >> 8; //TODO: do we need to take in account sprite x&y aligns ?
+ yAlign = (spriteInfo->yAlign * scale) >> 8; // ????
height = (spriteInfo->height * scale + 0x7f) >> 8;
width = (spriteInfo->width * scale + 0x7f) >> 8;
- scaleBuffer(spriteInfo->decodedBuffer, spriteInfo->width, spriteInfo->height, scale);
- buffer = _decodeBuf;
+ size_t outLength = width * height;
+ if (outLength > 0) {
+ scaleBuffer(&spriteInfo->decodedBuffer.front(), spriteInfo->width, spriteInfo->height, scale, outLength);
+ buffer = &_decodeBuf.front();
+ } else {
+ buffer = NULL;
+ }
} else {
xAlign = spriteInfo->xAlign;
yAlign = spriteInfo->yAlign;
height = spriteInfo->height;
width = spriteInfo->width;
- buffer = spriteInfo->decodedBuffer;
+ buffer = spriteInfo->decodedBuffer.getBuffer();
}
}
void Sprite::drawClip(const Point &spritePointer, int width, int height, const byte *spriteBuffer, bool clipToScene) {
- int clipWidth;
- int clipHeight;
Common::Rect clipRect = clipToScene ? _vm->_scene->getSceneClip() : _vm->getDisplayClip();
- int i, j, jo, io;
+ int xDstOffset, yDstOffset, xSrcOffset, ySrcOffset, xDiff, yDiff, cWidth, cHeight;
byte *bufRowPointer;
+ byte *bufPointer;
const byte *srcRowPointer;
+ const byte *srcPointer;
+
+ int backBufferPitch = _vm->_gfx->getBackBufferPitch();
+
+ //find Rects intersection
+ yDiff = clipRect.top - spritePointer.y;
+ if (yDiff > 0) {
+ ySrcOffset = yDiff;
+ yDstOffset = clipRect.top;
+ cHeight = height - yDiff;
+ } else {
+ ySrcOffset = 0;
+ yDstOffset = spritePointer.y;
+ cHeight = height;
+ }
- bufRowPointer = _vm->_gfx->getBackBufferPixels() + _vm->_gfx->getBackBufferPitch() * spritePointer.y;
- srcRowPointer = spriteBuffer;
-
- clipWidth = CLIP(width, 0, clipRect.right - spritePointer.x);
- clipHeight = CLIP(height, 0, clipRect.bottom - spritePointer.y);
-
- jo = 0;
- io = 0;
- if (spritePointer.x < clipRect.left) {
- jo = clipRect.left - spritePointer.x;
+ xDiff = clipRect.left - spritePointer.x;
+ if (xDiff > 0) {
+ xSrcOffset = xDiff;
+ xDstOffset = clipRect.left;
+ cWidth = width - xDiff;
+ } else {
+ xSrcOffset = 0;
+ xDstOffset = spritePointer.x;
+ cWidth = width;
}
- if (spritePointer.y < clipRect.top) {
- io = clipRect.top - spritePointer.y;
- bufRowPointer += _vm->_gfx->getBackBufferPitch() * io;
- srcRowPointer += width * io;
+
+ yDiff = yDstOffset + cHeight - clipRect.bottom;
+ if (yDiff > 0) {
+ cHeight -= yDiff;
}
- for (i = io; i < clipHeight; i++) {
- for (j = jo; j < clipWidth; j++) {
- assert(_vm->_gfx->getBackBufferPixels() <= (byte *)(bufRowPointer + j + spritePointer.x));
- assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width *
- _vm->getDisplayInfo().height)) > (byte *)(bufRowPointer + j + spritePointer.x));
- assert((const byte *)spriteBuffer <= (const byte *)(srcRowPointer + j));
- assert(((const byte *)spriteBuffer + (width * height)) > (const byte *)(srcRowPointer + j));
+ xDiff = xDstOffset + cWidth - clipRect.right;
+ if (xDiff > 0) {
+ cWidth -= xDiff;
+ }
- if (*(srcRowPointer + j) != 0) {
- *(bufRowPointer + j + spritePointer.x) = *(srcRowPointer + j);
+ if ((cHeight <= 0) || (cWidth <= 0)) {
+ //no intersection
+ return;
+ }
+ bufRowPointer = _vm->_gfx->getBackBufferPixels() + backBufferPitch * yDstOffset + xDstOffset;
+ srcRowPointer = spriteBuffer + width * ySrcOffset + xSrcOffset;
+
+ // validate src, dst buffers
+ assert(_vm->_gfx->getBackBufferPixels() <= bufRowPointer);
+ assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >=
+ (byte *)(bufRowPointer + backBufferPitch * (cHeight - 1) + cWidth));
+ assert((const byte *)spriteBuffer <= srcRowPointer);
+ assert(((const byte *)spriteBuffer + (width * height)) >= (const byte *)(srcRowPointer + width * (cHeight - 1) + cWidth));
+
+ const byte *srcPointerFinish2 = srcRowPointer + width * cHeight;
+ for (;;) {
+ srcPointer = srcRowPointer;
+ bufPointer = bufRowPointer;
+ const byte *srcPointerFinish = srcRowPointer + cWidth;
+ for (;;) {
+ if (*srcPointer != 0) {
+ *bufPointer = *srcPointer;
+ }
+ srcPointer++;
+ bufPointer++;
+ if (srcPointer == srcPointerFinish) {
+ break;
}
}
- bufRowPointer += _vm->_gfx->getBackBufferPitch();
srcRowPointer += width;
+ if (srcRowPointer == srcPointerFinish2) {
+ break;
+ }
+ bufRowPointer += backBufferPitch;
}
- int x1 = MAX<int>(spritePointer.x, 0);
- int y1 = MAX<int>(spritePointer.y, 0);
- int x2 = MIN<int>(MAX<int>(spritePointer.x + clipWidth, 0), clipRect.right);
- int y2 = MIN<int>(MAX<int>(spritePointer.y + clipHeight, 0), clipRect.bottom);
-
- if (x2 > x1 && y2 > y1)
- _vm->_render->addDirtyRect(Common::Rect(x1, y1, x2, y2));
+ _vm->_render->addDirtyRect(Common::Rect(xDstOffset, yDstOffset, xDstOffset + cWidth, yDstOffset + cHeight));
}
-void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale, bool clipToScene) {
+void Sprite::draw(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, bool clipToScene) {
const byte *spriteBuffer = NULL;
int width = 0;
int height = 0;
@@ -286,7 +299,7 @@ void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Point &scree
drawClip(spritePointer, width, height, spriteBuffer, clipToScene);
}
-void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale, bool clipToScene) {
+void Sprite::draw(SpriteList &spriteList, uint spriteNumber, const Rect &screenRect, int scale, bool clipToScene) {
const byte *spriteBuffer = NULL;
int width = 0;
int height = 0;
@@ -310,7 +323,7 @@ void Sprite::draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screen
drawClip(spritePointer, width, height, spriteBuffer, clipToScene);
}
-bool Sprite::hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint) {
+bool Sprite::hitTest(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, const Point &testPoint) {
const byte *spriteBuffer = NULL;
int i, j;
const byte *srcRowPointer;
@@ -337,7 +350,7 @@ bool Sprite::hitTest(SpriteList &spriteList, int spriteNumber, const Point &scre
return *srcRowPointer != 0;
}
-void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth) {
+void Sprite::drawOccluded(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, int depth) {
const byte *spriteBuffer = NULL;
int x, y;
byte *destRowPointer;
@@ -356,7 +369,6 @@ void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point
int maskWidth;
int maskHeight;
byte *maskBuffer;
- size_t maskBufferLength;
byte *maskRowPointer;
int maskZ;
@@ -365,7 +377,7 @@ void Sprite::drawOccluded(SpriteList &spriteList, int spriteNumber, const Point
return;
}
- _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer, maskBufferLength);
+ _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer);
getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
@@ -420,15 +432,11 @@ void Sprite::decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t ou
byte *outPointerEnd;
int c;
- if (outLength > _decodeBufLen) { // TODO: may we should make dynamic growing?
- error("Sprite::decodeRLEBuffer outLength > _decodeBufLen");
- }
-
- outPointer = _decodeBuf;
- outPointerEnd = _decodeBuf + outLength;
- outPointerEnd--;
+ _decodeBuf.resize(outLength);
+ outPointer = &_decodeBuf.front();
+ outPointerEnd = &_decodeBuf.back();
- memset(outPointer, 0, outLength);
+ memset(outPointer, 0, _decodeBuf.size());
MemoryReadStream readS(inputBuffer, inLength);
@@ -458,10 +466,14 @@ void Sprite::decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t ou
}
}
-void Sprite::scaleBuffer(const byte *src, int width, int height, int scale) {
+void Sprite::scaleBuffer(const byte *src, int width, int height, int scale, size_t outLength) {
byte skip = 256 - scale; // skip factor
byte vskip = 0x80, hskip;
- byte *dst = _decodeBuf;
+
+ _decodeBuf.resize(outLength);
+ byte *dst = &_decodeBuf.front();
+
+ memset(dst, 0, _decodeBuf.size());
for (int i = 0; i < height; i++) {
vskip += skip;
diff --git a/engines/saga/sprite.h b/engines/saga/sprite.h
index b7365fd28f..4e463cdd88 100644
--- a/engines/saga/sprite.h
+++ b/engines/saga/sprite.h
@@ -33,32 +33,19 @@ namespace Saga {
#define SPRITE_ZMAX 16
#define SPRITE_ZMASK 0x0F
-#define DECODE_BUF_LEN 64000
-
struct SpriteInfo {
- byte *decodedBuffer;
+ ByteArray decodedBuffer;
int width;
int height;
int xAlign;
int yAlign;
-};
-struct SpriteList {
- int spriteListResourceId;
- int spriteCount;
- SpriteInfo *infoList;
-
- void freeMem() {
- for (int i = 0; i < spriteCount; i++) {
- free(infoList[i].decodedBuffer);
- }
- free(infoList);
- memset(this, 0, sizeof(*this));
+ SpriteInfo() : width(0), height(0), xAlign(0), yAlign(0) {
}
+};
- SpriteList() {
- memset(this, 0, sizeof(*this));
- }
+class SpriteList : public Common::Array<SpriteInfo> {
+// int spriteListResourceId;
};
@@ -73,28 +60,27 @@ public:
~Sprite();
// draw scaled sprite using background scene mask
- void drawOccluded(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth);
+ void drawOccluded(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, int depth);
// draw scaled sprite using background scene mask
- void draw(SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale, bool clipToScene = false);
+ void draw(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, bool clipToScene = false);
// main function
void drawClip(const Point &spritePointer, int width, int height, const byte *spriteBuffer, bool clipToScene = false);
- void draw(SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale, bool clipToScene = false);
+ void draw(SpriteList &spriteList, uint spriteNumber, const Rect &screenRect, int scale, bool clipToScene = false);
void loadList(int resourceId, SpriteList &spriteList); // load or append spriteList
- bool hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint);
- void getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer);
+ bool hitTest(SpriteList &spriteList, uint spriteNumber, const Point &screenCoord, int scale, const Point &testPoint);
+ void getScaledSpriteBuffer(SpriteList &spriteList, uint spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer);
private:
void decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t outLength);
- void scaleBuffer(const byte *src, int width, int height, int scale);
+ void scaleBuffer(const byte *src, int width, int height, int scale, size_t outLength);
SagaEngine *_vm;
ResourceContext *_spriteContext;
- byte *_decodeBuf;
- size_t _decodeBufLen;
+ ByteArray _decodeBuf;
};
} // End of namespace Saga
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
index be674e5acd..098970f4e8 100644
--- a/engines/saga/sthread.cpp
+++ b/engines/saga/sthread.cpp
@@ -39,16 +39,18 @@ namespace Saga {
ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
loadModule(scriptModuleNumber);
- if (_modules[scriptModuleNumber].entryPointsCount <= scriptEntryPointNumber) {
+ if (_modules[scriptModuleNumber].entryPoints.size() <= scriptEntryPointNumber) {
error("Script::createThread wrong scriptEntryPointNumber");
}
- ScriptThread newThread;
+ ScriptThread tmp;
+ _threadList.push_front(tmp);
+ ScriptThread &newThread = _threadList.front();
newThread._instructionOffset = _modules[scriptModuleNumber].entryPoints[scriptEntryPointNumber].offset;
- newThread._commonBase = _commonBuffer;
- newThread._staticBase = _commonBuffer + _modules[scriptModuleNumber].staticOffset;
- newThread._moduleBase = _modules[scriptModuleNumber].moduleBase;
- newThread._moduleBaseSize = _modules[scriptModuleNumber].moduleBaseSize;
+ newThread._commonBase = _commonBuffer.getBuffer();
+ newThread._staticBase = _commonBuffer.getBuffer() + _modules[scriptModuleNumber].staticOffset;
+ newThread._moduleBase = _modules[scriptModuleNumber].moduleBase.getBuffer();
+ newThread._moduleBaseSize = _modules[scriptModuleNumber].moduleBase.size();
newThread._strings = &_modules[scriptModuleNumber].strings;
if (_vm->getGameId() == GID_IHNM)
@@ -56,14 +58,10 @@ ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntry
else
newThread._voiceLUT = &_modules[scriptModuleNumber].voiceLUT;
- _threadList.push_front(newThread);
-
+ newThread._stackBuf.resize(ScriptThread::THREAD_STACK_SIZE);
+ newThread._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
debug(3, "createThread(). Total threads: %d", _threadList.size());
-
- ScriptThread &tmp = *_threadList.begin();
- tmp._stackBuf = (int16 *)malloc(ScriptThread::THREAD_STACK_SIZE * sizeof(int16));
- tmp._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
- return tmp;
+ return newThread;
}
void Script::wakeUpActorThread(int waitType, void *threadObj) {
@@ -211,7 +209,7 @@ bool Script::runThread(ScriptThread &thread) {
savedInstructionOffset = thread._instructionOffset;
operandChar = scriptS.readByte();
- debug(8, "Executing thread offset: %u (%x) stack: %d", thread._instructionOffset, operandChar, thread.pushedSize());
+ debug(8, "Executing thread offset: %u (0x%X) stack: %d", thread._instructionOffset, operandChar, thread.pushedSize());
stopParsing = false;
debug(4, "Calling op %s", this->_scriptOpsList[operandChar].scriptOpName);
diff --git a/engines/savestate.cpp b/engines/savestate.cpp
index 3cd81a2ff6..368f59ef51 100644
--- a/engines/savestate.cpp
+++ b/engines/savestate.cpp
@@ -70,3 +70,9 @@ void SaveStateDescriptor::setPlayTime(int hours, int minutes) {
snprintf(buffer, 32, "%.2d:%.2d", hours, minutes);
setVal("play_time", buffer);
}
+
+void SaveStateDescriptor::setPlayTime(uint32 msecs) {
+ uint minutes = msecs / 60000;
+ setPlayTime(minutes / 60, minutes % 60);
+}
+
diff --git a/engines/savestate.h b/engines/savestate.h
index ddbcea1acf..37f2b9bdd4 100644
--- a/engines/savestate.h
+++ b/engines/savestate.h
@@ -127,6 +127,11 @@ public:
* Sets the 'play_time' key properly, based on the given values.
*/
void setPlayTime(int hours, int minutes);
+
+ /**
+ * Sets the 'play_time' key properly, based on the given value.
+ */
+ void setPlayTime(uint32 msecs);
};
/** List of savestates. */
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 6246754ec4..deab3dadd8 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -39,6 +39,7 @@
#include "sci/sound/midiparser_sci.h"
#include "sci/sound/music.h"
#include "sci/sound/drivers/mididriver.h"
+#include "sci/sound/drivers/map-mt32-to-gm.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint.h"
@@ -67,7 +68,7 @@ bool g_debug_track_mouse_clicks = false;
static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeValue);
Console::Console(SciEngine *engine) : GUI::Debugger(),
- _engine(engine), _debugState(engine->_debugState), _enterTime(0) {
+ _engine(engine), _debugState(engine->_debugState) {
// Variables
DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0);
@@ -98,12 +99,11 @@ 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));
DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts));
- DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
// Game
DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame));
DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame));
@@ -145,6 +145,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("stopallsounds", WRAP_METHOD(Console, cmdStopAllSounds));
DCmd_Register("sfx01_header", WRAP_METHOD(Console, cmdSfx01Header));
DCmd_Register("sfx01_track", WRAP_METHOD(Console, cmdSfx01Track));
+ DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
+ DCmd_Register("map_instrument", WRAP_METHOD(Console, cmdMapInstrument));
// Script
DCmd_Register("addresses", WRAP_METHOD(Console, cmdAddresses));
DCmd_Register("registers", WRAP_METHOD(Console, cmdRegisters));
@@ -166,6 +168,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("snk", WRAP_METHOD(Console, cmdStepCallk)); // alias
DCmd_Register("disasm", WRAP_METHOD(Console, cmdDisassemble));
DCmd_Register("disasm_addr", WRAP_METHOD(Console, cmdDisassembleAddress));
+ DCmd_Register("find_callk", WRAP_METHOD(Console, cmdFindKernelFunctionCall));
DCmd_Register("send", WRAP_METHOD(Console, cmdSend));
DCmd_Register("go", WRAP_METHOD(Console, cmdGo));
DCmd_Register("logkernel", WRAP_METHOD(Console, cmdLogKernel));
@@ -218,15 +221,10 @@ Console::~Console() {
}
void Console::preEnter() {
- if (g_sci && g_sci->_soundCmd)
- g_sci->_soundCmd->pauseAll(true);
- _enterTime = g_system->getMillis();
+ _engine->pauseEngine(true);
}
void Console::postEnter() {
- if (g_sci && g_sci->_soundCmd)
- g_sci->_soundCmd->pauseAll(false);
-
if (!_videoFile.empty()) {
_engine->_gfxCursor->kernelHide();
@@ -283,8 +281,7 @@ void Console::postEnter() {
_videoFrameDelay = 0;
}
- // Subtract the time we were running the debugger from the game running time
- _engine->_gamestate->gameStartTime += g_system->getMillis() - _enterTime;
+ _engine->pauseEngine(false);
}
bool Console::cmdHelp(int argc, const char **argv) {
@@ -327,12 +324,11 @@ 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");
DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n");
- DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n");
DebugPrintf("\n");
DebugPrintf("Game:\n");
DebugPrintf(" save_game - Saves the current game state to the hard disk\n");
@@ -372,6 +368,8 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" is_sample - Shows information on a given sound resource, if it's a PCM sample\n");
DebugPrintf(" sfx01_header - Dumps the header of a SCI01 song\n");
DebugPrintf(" sfx01_track - Dumps a track of a SCI01 song\n");
+ DebugPrintf(" show_instruments - Shows the instruments of a specific song, or all songs\n");
+ DebugPrintf(" map_instrument - Dynamically maps an MT-32 instrument to a GM instrument\n");
DebugPrintf("\n");
DebugPrintf("Script:\n");
DebugPrintf(" addresses - Provides information on how to pass addresses\n");
@@ -647,7 +645,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);
}
@@ -724,9 +722,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;
}
@@ -740,6 +738,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);
}
@@ -862,6 +861,14 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
return true;
}
+// Same as in sound/drivers/midi.cpp
+uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins) {
+ if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM)
+ return Mt32Ins.gmRhythmKey + 0x80;
+ else
+ return Mt32Ins.gmInstr;
+}
+
bool Console::cmdShowInstruments(int argc, const char **argv) {
int songNumber = -1;
@@ -1004,7 +1011,16 @@ bool Console::cmdShowInstruments(int argc, const char **argv) {
DebugPrintf("%d, ", i);
}
DebugPrintf("\n\n");
+ }
+
+ DebugPrintf("Instruments not mapped in the MT32->GM map: ");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0 && getGmInstrument(Mt32MemoryTimbreMaps[i]) == MIDI_UNMAPPED)
+ DebugPrintf("%d, ", i);
+ }
+ DebugPrintf("\n\n");
+ if (songNumber == -1) {
DebugPrintf("Used instruments in songs:\n");
for (int i = 0; i < 128; i++) {
if (instruments[i] > 0) {
@@ -1024,6 +1040,43 @@ bool Console::cmdShowInstruments(int argc, const char **argv) {
return true;
}
+bool Console::cmdMapInstrument(int argc, const char **argv) {
+ if (argc != 4) {
+ DebugPrintf("Maps an MT-32 custom instrument to a GM instrument on the fly\n\n");
+ DebugPrintf("Usage %s <MT-32 instrument name> <GM instrument> <GM rhythm key>\n", argv[0]);
+ DebugPrintf("Each MT-32 instrument is always 10 characters and is mapped to either a GM instrument, or a GM rhythm key\n");
+ DebugPrintf("A value of 255 (0xff) signifies an unmapped instrument\n");
+ DebugPrintf("Please replace the spaces in the instrument name with underscores (\"_\"). They'll be converted to spaces afterwards\n\n");
+ DebugPrintf("Example: %s test_0__XX 1 255\n", argv[0]);
+ DebugPrintf("The above example will map the MT-32 instrument \"test 0 XX\" to GM instrument 1\n\n");
+ } else {
+ if (Mt32dynamicMappings != NULL) {
+ Mt32ToGmMap newMapping;
+ char *instrumentName = new char[11];
+ Common::strlcpy(instrumentName, argv[1], 11);
+
+ for (uint16 i = 0; i < strlen(instrumentName); i++)
+ if (instrumentName[i] == '_')
+ instrumentName[i] = ' ';
+
+ newMapping.name = instrumentName;
+ newMapping.gmInstr = atoi(argv[2]);
+ newMapping.gmRhythmKey = atoi(argv[3]);
+ Mt32dynamicMappings->push_back(newMapping);
+ }
+ }
+
+ DebugPrintf("Current dynamic mappings:\n");
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ DebugPrintf("\"%s\" -> %d / %d\n", (*it).name, (*it).gmInstr, (*it).gmRhythmKey);
+ }
+ }
+
+ return true;
+}
+
bool Console::cmdList(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Lists all the resources of a given type\n");
@@ -1101,7 +1154,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;
}
@@ -1133,18 +1186,24 @@ 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);
}
bool Console::cmdClassTable(int argc, const char **argv) {
- DebugPrintf("Available classes:\n");
+ DebugPrintf("Available classes (parse a parameter to filter the table by a specific class):\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) {
+ const char *className = _engine->_gamestate->_segMan->getObjectName(temp.reg);
+ if (argc == 1 || (argc == 2 && !strcmp(className, argv[1]))) {
+ DebugPrintf(" Class 0x%x (%s) at %04x:%04x (script %d)\n", i,
+ className,
+ PRINT_REG(temp.reg),
+ temp.script);
+ }
}
}
@@ -1201,7 +1260,6 @@ bool Console::cmdParse(int argc, const char **argv) {
return true;
}
- ResultWordList words;
char *error;
char string[1000];
@@ -1213,6 +1271,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;
@@ -1221,8 +1281,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
@@ -1249,7 +1314,6 @@ bool Console::cmdSaid(int argc, const char **argv) {
return true;
}
- ResultWordList words;
char *error;
char string[1000];
byte spec[1000];
@@ -1323,6 +1387,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;
@@ -1331,8 +1396,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
@@ -2621,11 +2693,88 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
return true;
}
+bool Console::cmdFindKernelFunctionCall(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Finds the scripts and methods that call a specific kernel function.\n");
+ DebugPrintf("Usage: %s <kernel function>\n", argv[0]);
+ DebugPrintf("Example: %s Display\n", argv[0]);
+ return true;
+ }
+
+ // Find the number of the kernel function call
+ int kernelFuncNum = _engine->getKernel()->findKernelFuncPos(argv[1]);
+
+ if (kernelFuncNum < 0) {
+ DebugPrintf("Invalid kernel function requested");
+ return true;
+ }
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ DebugPrintf("%d scripts found, dissassembling...\n", resources->size());
+
+ int scriptSegment;
+ Script *script;
+ SegManager *segMan = _engine->getEngineState()->_segMan;
+
+ while (itr != resources->end()) {
+ // Load script
+ scriptSegment = segMan->instantiateScript(itr->getNumber());
+ script = segMan->getScript(scriptSegment);
+
+ // Iterate through all the script's objects
+ ObjMap::iterator it;
+ const ObjMap::iterator end = script->_objects.end();
+ for (it = script->_objects.begin(); it != end; ++it) {
+ const Object *obj = segMan->getObject(it->_value.getPos());
+ const char *objName = segMan->getObjectName(it->_value.getPos());
+
+ // Now dissassemble each method of the script object
+ for (uint16 i = 0; i < obj->getMethodCount(); i++) {
+ reg_t fptr = obj->getFunction(i);
+ uint16 offset = fptr.offset;
+ int16 opparams[4];
+ byte extOpcode;
+ byte opcode;
+
+ while (true) {
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
+ opcode = extOpcode >> 1;
+
+ if (opcode == op_callk) {
+ uint16 kFuncNum = opparams[0];
+ uint16 argc2 = opparams[1];
+
+ if (kFuncNum == kernelFuncNum) {
+ DebugPrintf("Called from script %d, object %s, method %s(%d) with %d parameters\n",
+ itr->getNumber(), objName,
+ _engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), i, argc2);
+ }
+ }
+
+ // Check for end of function/script
+ if (opcode == op_ret || offset >= script->getBufSize())
+ break;
+ } // while (true)
+ } // for (uint16 i = 0; i < obj->getMethodCount(); i++)
+ } // for (it = script->_objects.begin(); it != end; ++it)
+
+ segMan->uninstantiateScript(itr->getNumber());
+ ++itr;
+ }
+
+ delete resources;
+
+ return true;
+}
+
bool Console::cmdSend(int argc, const char **argv) {
if (argc < 3) {
DebugPrintf("Sends a message to an object.\n");
DebugPrintf("Usage: %s <object> <selector name> <param1> <param2> ... <paramn>\n", argv[0]);
- DebugPrintf("Example: send ?fooScript cue\n");
+ DebugPrintf("Example: %s ?fooScript cue\n", argv[0]);
return true;
}
diff --git a/engines/sci/console.h b/engines/sci/console.h
index 444443154c..68c2d21e38 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -70,12 +70,11 @@ 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);
bool cmdVerifyScripts(int argc, const char **argv);
- bool cmdShowInstruments(int argc, const char **argv);
// Game
bool cmdSaveGame(int argc, const char **argv);
bool cmdRestoreGame(int argc, const char **argv);
@@ -115,6 +114,8 @@ private:
bool cmdStopAllSounds(int argc, const char **argv);
bool cmdSfx01Header(int argc, const char **argv);
bool cmdSfx01Track(int argc, const char **argv);
+ bool cmdShowInstruments(int argc, const char **argv);
+ bool cmdMapInstrument(int argc, const char **argv);
// Script
bool cmdAddresses(int argc, const char **argv);
bool cmdRegisters(int argc, const char **argv);
@@ -128,6 +129,7 @@ private:
bool cmdStepCallk(int argc, const char **argv);
bool cmdDisassemble(int argc, const char **argv);
bool cmdDisassembleAddress(int argc, const char **argv);
+ bool cmdFindKernelFunctionCall(int argc, const char **argv);
bool cmdSend(int argc, const char **argv);
bool cmdGo(int argc, const char **argv);
bool cmdLogKernel(int argc, const char **argv);
@@ -166,7 +168,6 @@ private:
bool _mouseVisible;
Common::String _videoFile;
int _videoFrameDelay;
- uint32 _enterTime;
};
} // End of namespace Sci
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index fbef406ee7..2292692ac1 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -516,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
@@ -526,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";
@@ -624,7 +624,8 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const {
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
- (f == kSavesSupportCreationDate);
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime);
}
bool SciEngine::hasFeature(EngineFeature f) const {
@@ -665,7 +666,7 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
delete in;
continue;
}
- saveList.push_back(SaveStateDescriptor(slotNum, meta.savegame_name));
+ saveList.push_back(SaveStateDescriptor(slotNum, meta.name));
delete in;
}
}
@@ -688,7 +689,7 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
return desc;
}
- SaveStateDescriptor desc(slot, meta.savegame_name);
+ SaveStateDescriptor desc(slot, meta.name);
Graphics::Surface *thumbnail = new Graphics::Surface();
assert(thumbnail);
@@ -702,18 +703,18 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
desc.setDeletableFlag(true);
desc.setWriteProtectedFlag(false);
- int day = (meta.savegame_date >> 24) & 0xFF;
- int month = (meta.savegame_date >> 16) & 0xFF;
- int year = meta.savegame_date & 0xFFFF;
+ int day = (meta.saveDate >> 24) & 0xFF;
+ int month = (meta.saveDate >> 16) & 0xFF;
+ int year = meta.saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
- int hour = (meta.savegame_time >> 16) & 0xFF;
- int minutes = (meta.savegame_time >> 8) & 0xFF;
+ int hour = (meta.saveTime >> 16) & 0xFF;
+ int minutes = (meta.saveTime >> 8) & 0xFF;
desc.setSaveTime(hour, minutes);
- // TODO: played time
+ desc.setPlayTime(meta.playTime * 1000);
delete in;
@@ -765,7 +766,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 d489ba219d..79acbf41b7 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -752,10 +752,10 @@ 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)
+ // Hoyle 1 3.5' - English DOS (supplied by eddydrama in bug report #3052366 and dinnerx in bug report #3090841)
{"hoyle1", "", {
{"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386},
- {"resource.001", 0, "e0dd44069a62463fd124974b915f10d", 518127},
+ {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518127},
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
@@ -854,14 +854,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Hoyle 4 - English DOS Demo
+ // Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
{"hoyle4", "Demo", {
{"resource.map", 0, "60f764020a6b788bbbe415dbc2ccb9f3", 931},
{"resource.000", 0, "5fe3670e3ddcd4f85c10013b5453141a", 615522},
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Hoyle 4 - English DOS Demo
+ // Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
// SCI interpreter version 1.001.200 (just a guess)
// Does anyone have this version? -clone2727
{"hoyle4", "Demo", {
@@ -871,7 +871,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
// Hoyle 4 (Hoyle Classic Card Games) - English DOS/Win
- // SCI1.1
// Supplied by abevi in bug report #3039291
{"hoyle4", "", {
{"resource.map", 0, "2b577c975cc8d8d43f61b6a756129fe3", 4352},
@@ -879,6 +878,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Hoyle 4 (Hoyle Classic Card Games) - English Macintosh Floppy
+ // VERSION file reports "2.0"
+ {"hoyle4", "", {
+ {"Data1", 0, "afad082944d36ce4d2a9e646efc49da1", 7731536},
+ {"Data2", 0, "615ed2efe969f845cd8f0686af0b06f2", 1543825},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO_NOSPEECH },
+
// Jones in the Fast Lane EGA - English DOS
// SCI interpreter version 1.000.172 (not 100% sure FIXME)
{"jones", "EGA", {
@@ -1154,6 +1161,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", {
@@ -1265,6 +1284,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
@@ -1449,20 +1480,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Laura Bow - German DOS (from Tobis87, also includes english language)
- // SCI interpreter version 0.000.631 (or 0.000.685?)
- {"laurabow", "", {
- {"resource.map", 0, "b1905f6aa68ff65a057b080b1eae954c", 12030},
- {"resource.001", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 108032},
- {"resource.002", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 354680},
- {"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 361815},
- {"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 339714},
- {"resource.005", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 327465},
- {"resource.006", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 328390},
- {"resource.007", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 317687},
- AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
-
// Laura Bow 2 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
// SCI interpreter version 1.001.069 (just a guess)
@@ -3309,6 +3326,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
+ // Space Quest 4 - Russian DOS
+ // Executable scanning reports "1.000.753", VERSION file reports "1.994"
+ {"sq4", "", {
+ {"resource.map", 0, "e4f77dd99012d51e16903da07769a7bf", 5928},
+ {"resource.000", 0, "e1f46832cd2458796028e054a0466031", 186750},
+ {"resource.001", 0, "1110371c3bafbbf8968a324097c83fdb", 1283759},
+ {"resource.002", 0, "9c342cd76b421369406d6fafd7b1a285", 1234726},
+ {"resource.003", 0, "e617f09840d9f86181f7602c8bf2e8ad", 1266491},
+ {"resource.004", 0, "2763fe4f0cb74df716ec8b0c464b0988", 1217428},
+ {"resource.005", 0, "d608713197c5ba1cd8c6ed46299c3069", 1057924},
+ AD_LISTEND},
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Space Quest 5 - English DOS (from the Space Quest Collection)
// Executable scanning reports "1.001.068", VERSION file reports "1.04"
{"sq5", "", {
@@ -3352,6 +3382,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Space Quest 5 - Spanish DOS Floppy (from mirir, bug report #3090664)
+ {"sq5", "", {
+ {"resource.000", 0, "73748852548faa42927f7537b165582d", 6049994},
+ {"resource.map", 0, "5714a899033bdebf2d61ad333c8c6637", 6492},
+ {"resource.msg", 0, "46deca7ef9cf057f7d442df98c1a2ae2", 134612},
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Space Quest 5 - Russian DOS
+ // Executable scanning reports "1.001.068", VERSION file reports "1.994"
+ {"sq5", "", {
+ {"resource.map", 0, "82e6e9b4270a4007578a119b6a51860c", 6493},
+ {"resource.000", 0, "6f9ed21e1001526b4137f6703ed476af", 6103778},
+ {"resource.msg", 0, "0a8931990cd2eac1691602391c68ab85", 147580},
+ AD_LISTEND},
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
#ifdef ENABLE_SCI32
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3499,6 +3546,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
FANMADE("SCI Studio Template 3.0", "ca0dc8d586e0a8670b7621cde090b532", 354, "58a48ee692a86c0575e6bd0b00a92b9a", 113097),
FANMADE("SCI Quest", "9067e1f1e54436d2dbfce855524bc84a", 552, "ffa7d355cd9223f245289108a696bcd2", 149634),
FANMADE("SCI-Man", "3ab85bd39a86c11f85781764f9db09bb", 468, "bb8f9992f504a242bf0860e3588e150b", 131810),
+ FANMADE("The Black Cauldron", "5e1ff2833c7f33ebcfa456ba836e2067", 2592, "2f8e6264d2db91bb54982ab8aa18b3b4", 1881839),
FANMADE("The Farm Nightmare", "fb6cbfddaa7c055e2c3d8cf4c683a7db", 906, "50655e8b8925f717e698e08f006f40be", 338303),
FANMADE("The Gem Scenario", "ef5f61f4d2c6d31122d3e2baf89ad976", 642, "2f16be390dd90c3d7ca1c8a594ac0bfa", 244794),
FANMADE("The Legend of the Lost Jewel", "ba1bca315e3818c5626eda51bcfbcccf", 636, "9b0736d69924af0cff32a0f78db96855", 300398),
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index ff327a0049..b1ee6abaff 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -88,6 +88,14 @@ const Common::String &Kernel::getKernelName(uint number) const {
return _kernelNames[number];
}
+int Kernel::findKernelFuncPos(Common::String kernelFuncName) {
+ for (uint32 i = 0; i < _kernelNames.size(); i++)
+ if (_kernelNames[i] == kernelFuncName)
+ return i;
+
+ return -1;
+}
+
int Kernel::findSelector(const char *selectorName) const {
for (uint pos = 0; pos < _selectorNames.size(); ++pos) {
if (_selectorNames[pos] == selectorName)
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index dedc836b32..9833f91a5e 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -151,6 +151,7 @@ public:
uint getSelectorNamesSize() const;
const Common::String &getSelectorName(uint selector);
+ int findKernelFuncPos(Common::String kernelFuncName);
uint getKernelNamesSize() const;
const Common::String &getKernelName(uint number) const;
@@ -278,7 +279,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);
@@ -420,6 +420,7 @@ 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);
@@ -444,13 +445,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
@@ -459,13 +461,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 29e2a38ad4..724ceee878 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -27,6 +27,7 @@
#define SCI_ENGINE_KERNEL_TABLES_H
#include "sci/engine/workarounds.h"
+#include "sci/engine/vm.h" // for opcode_formats
namespace Sci {
@@ -81,201 +82,197 @@ 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[] = {
- { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
- { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
- { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL },
- { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
- { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
- { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
- { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
- { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
- { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
- { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
- { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
- { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
- { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
- // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
- // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
- // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
- // it actually does internally
- { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
- { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
- { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
- { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL },
- { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
- { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
- // ^^ Longbow demo
- { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL },
- { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
- { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
- { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
- { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
- { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
+ { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
+ { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL },
+ { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
+ { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
+ { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
+ { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
+ { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
+ { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
+ { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
+ { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
+ { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
+ { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
+ // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
+ // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
+ // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
+ // it actually does internally
+ { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
+ { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
+ { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL },
+ { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
+ // ^^ Longbow demo
+ { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL },
+ { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
+ { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
+ { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
+ { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
#ifdef ENABLE_SCI32
- { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
- { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL },
- { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
- { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL },
- { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL },
- { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL },
- // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the
- // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although
- // I guess there are many more changes somewhere
- // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly
- // signature for SCI21 should be "o"
- { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL },
- { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL },
- { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
- { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
- { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL },
- { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL },
- { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL },
- { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
- { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
- { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
- { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL },
+ { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL },
+ { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL },
+ { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL },
+ // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the
+ // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although
+ // I guess there are many more changes somewhere
+ // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly
+ // signature for SCI21 should be "o"
+ { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL },
+ { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL },
+ { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL },
+ { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL },
+ { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
+ { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
#endif
- SCI_SUBOPENTRY_TERMINATOR
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kGraph_subops[] = {
- { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
- { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
- // 3 - set palette via resource
- { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
- // 5 - nop
- // 6 - draw pattern
- { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
- { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
- // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
- { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
- { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
- { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
- { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", kGraphUpdateBox_workarounds }, // kq6 hires
- { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
- { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
- { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
- { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
+ { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
+ // 3 - set palette via resource
+ { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
+ // 5 - nop
+ // 6 - draw pattern
+ { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
+ { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
+ // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
+ { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
+ { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
+ { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", kGraphUpdateBox_workarounds }, // kq6 hires
+ { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", kGraphUpdateBox_workarounds },
+ { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
+ { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalVary_subops[] = {
- { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
- { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
- { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
- { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
- { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalette_subops[] = {
- { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
- { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
- { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
- { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
+ { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
static const SciKernelMapSubEntry kFileIO_subops[] = {
- { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
- { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
- { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
- { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
- { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
- { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
- { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
- { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
- { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
- { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
- { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
- { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
- { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
+ { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
+ { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
+ { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
#ifdef ENABLE_SCI32
- { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
- { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
- { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
- { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
- { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
+ { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
+ { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
+ { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
+ { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
#endif
- SCI_SUBOPENTRY_TERMINATOR
+ SCI_SUBOPENTRY_TERMINATOR
};
#ifdef ENABLE_SCI32
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
- { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
- { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
- { SIG_SCI21, 2, MAP_CALL(NewNode), ".", NULL },
- { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
- { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
- { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
- { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
- { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
- { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
- { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
- { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
- { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln.", NULL },
- { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
- { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
- { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
- { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
- { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
- { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
- // FIXME: This doesn't seem to be ListIndexOf. In Torin demo, an index is
- // passed as a second parameter instead of an object. Thus, it seems to
- // be something like ListAt instead... If we swap the two subops though,
- // Torin demo crashes complaining that it tried to send to a non-object,
- // therefore the semantics might be different here (signature was l[o0])
- // In SQ6 object is passed right when skipping the intro
- { SIG_SCI21, 18, MAP_CALL(StubNull), "l[io]", NULL },
- { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL },
- { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL },
- { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL },
- { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
- SCI_SUBOPENTRY_TERMINATOR
+ { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
+ { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
+ { SIG_SCI21, 2, MAP_CALL(NewNode), ".(.)", NULL },
+ { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
+ { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
+ { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
+ { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
+ { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
+ { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
+ { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
+ { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL },
+ { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
+ { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
+ { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
+ { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
+ { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
+ { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
+ { 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 },
+ { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
#endif
@@ -294,202 +291,299 @@ struct SciKernelMapEntry {
// name, version/platform, signature, sub-signatures, workarounds
static SciKernelMapEntry s_kernelMap[] = {
- { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
- { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
- { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
- { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
- { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
- { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
- { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
+ { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
+ { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
+ { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
+ { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
+ { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
#ifdef ENABLE_SCI32
- { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+ { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
#endif
- { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
- { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds },
- { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds },
- { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
- { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL },
- { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
- { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
- { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
- { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, kDeviceInfo_workarounds }, // subop
- { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, 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, 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(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
- { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
- { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
- { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
- { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
- { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
- { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
- { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
- { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
- { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
- { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
- { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
- { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
- { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
- { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
- { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
- { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
- { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
- { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
- { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
- { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
- { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
- { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
- { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
- { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
- { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
- { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
- { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
- { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
- { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
- { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
- { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
- { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL },
- { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
- { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
- { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
- { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", 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
- { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
- { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
- { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
- { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
- { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds },
+ { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
+ { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, kDeviceInfo_workarounds }, // subop
+ { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, 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, 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(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
+ { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
+ { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
+ { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
+ { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
+ { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
+ { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
+ { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
+ { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
+ { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
+ { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
+ { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
+ { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
+ { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
+ { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
+ { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
+ { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
+ { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
+ { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
+ { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
+ { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
+ { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
+ { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
+ { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
+ { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
+ { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
+ { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL },
+ { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(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, "[r0]i[r0]", NULL, NULL },
+ { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", 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
+ { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+ { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
+ { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(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, 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 },
- { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
- { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
- { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
- { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
- { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+ { 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, 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 },
+ { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
+ { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
+ { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
+ { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+
+ // 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
- { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
- { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
- { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
- { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
- { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ // SCI2 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 },
+ { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
+ { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
+ { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
+ { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL },
+ { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii([ri])(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 unmapped functions - TODO!
+ // SetScroll
+ // AddMagnify // most probably similar to the SCI1.1 functions. We need a test case
+ // DeleteMagnify
+ // EditText
+ // DisposeTextBitmap
+ // VibrateMouse - used in QFG4 floppy
+ // PalCycle
+ // ObjectIntersect - used in QFG4 floppy
+
+ // 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 },
+
+ // Unused SCI2 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(GetHighItemPri), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(MakeSaveCatName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(MakeSaveFileName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- // SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
- { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
- { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
- { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
- { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+ // SCI2.1 Kernel Functions
+ { 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 },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { 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 unmapped functions - TODO!
+ // SetLanguage
+ // FindSelector
+ // FindClass
+ // CelRect
+ // BaseLineSpan
+ // CelInfo
+ // Bitmap
+ // CelLink
+ // MovePlaneItems
+ // Font
+ // ScrollWindow
+ // AddLine
+ // DeleteLine
+ // UpdateLine
+ // AddPolygon
+ // DeletePolygon
+ // UpdatePolygon
+ // GetConfig
+ // Table
+ // LoadChunk
+ // SetPalStyleRange
+ // NewRoom
+ // Priority
+ // MorphOn
+ // SetHotRectangles
+ // DeletePic
+
+ // 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 },
+
+ // Unused SCI2.1 unused functions, always mapped to kDummy
+ { 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. */
@@ -575,20 +669,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",
@@ -609,33 +703,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
@@ -679,7 +773,7 @@ static const char *sci2_default_knames[] = {
/*0x21*/ "DeleteMagnify",
/*0x22*/ "IsHiRes",
/*0x23*/ "Graph",
- /*0x24*/ "InvertRect",
+ /*0x24*/ "InvertRect", // only in SCI2, not used in any SCI2 game
/*0x25*/ "TextSize",
/*0x26*/ "Message",
/*0x27*/ "TextColors",
@@ -701,8 +795,8 @@ static const char *sci2_default_knames[] = {
/*0x37*/ "RestoreGame",
/*0x38*/ "RestartGame",
/*0x39*/ "GameIsRestarting",
- /*0x3a*/ "MakeSaveCatName",
- /*0x3b*/ "MakeSaveFileName",
+ /*0x3a*/ "MakeSaveCatName", // only in SCI2, not used in any SCI2 game
+ /*0x3b*/ "MakeSaveFileName", // only in SCI2, not used in any SCI2 game
/*0x3c*/ "GetSaveFiles",
/*0x3d*/ "GetSaveDir",
/*0x3e*/ "CheckSaveGame",
@@ -758,13 +852,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",
@@ -776,12 +870,12 @@ 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",
- /*0x89*/ "TextWidth",
- /*0x8a*/ "PointSize",
+ /*0x88*/ "MarkMemory", // for debugging
+ /*0x89*/ "TextWidth", // for debugging(?), only in SCI2, not used in any SCI2 game
+ /*0x8a*/ "PointSize", // for debugging(?), only in SCI2, not used in any SCI2 game
// GK2 Demo (and similar) only kernel functions
/*0x8b*/ "AddLine",
@@ -935,7 +1029,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",
@@ -957,8 +1051,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/kfile.cpp b/engines/sci/engine/kfile.cpp
index e08f27cdf7..3b41c851e1 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -28,6 +28,7 @@
#include "common/file.h"
#include "common/str.h"
#include "common/savefile.h"
+#include "common/translation.h"
#include "gui/saveload.h"
@@ -421,7 +422,7 @@ static void listSavegames(Common::Array<SavegameDesc> &saves) {
Common::SeekableReadStream *in;
if ((in = saveFileMan->openForLoading(filename))) {
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
+ if (!get_savegame_metadata(in, &meta) || meta.name.empty()) {
// invalid
delete in;
continue;
@@ -430,17 +431,17 @@ static void listSavegames(Common::Array<SavegameDesc> &saves) {
SavegameDesc desc;
desc.id = strtol(filename.end() - 3, NULL, 10);
- desc.date = meta.savegame_date;
+ desc.date = meta.saveDate;
// We need to fix date in here, because we save DDMMYYYY instead of
// YYYYMMDD, so sorting wouldn't work
desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
- desc.time = meta.savegame_time;
- desc.version = meta.savegame_version;
+ desc.time = meta.saveTime;
+ desc.version = meta.version;
- if (meta.savegame_name.lastChar() == '\n')
- meta.savegame_name.deleteLastChar();
+ if (meta.name.lastChar() == '\n')
+ meta.name.deleteLastChar();
- Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+ Common::strlcpy(desc.name, meta.name.c_str(), SCI_MAX_SAVENAME_LENGTH);
debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
@@ -495,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)
@@ -573,10 +574,18 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
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");
+ 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)
@@ -636,11 +645,11 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
warning("Error opening savegame \"%s\" for writing", filename.c_str());
} else {
if (!gamestate_save(s, out, game_description.c_str(), version.c_str())) {
- warning("Saving the game failed.");
+ warning("Saving the game failed");
} else {
out->finalize();
if (out->err()) {
- warning("Writing the savegame failed.");
+ warning("Writing the savegame failed");
} else {
s->r_acc = TRUE_REG; // success
}
@@ -665,7 +674,7 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
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");
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"));
dialog->setSaveMode(false);
savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
delete dialog;
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 02891dde9e..282ca0f842 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -129,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();
@@ -184,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);
@@ -347,6 +341,11 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
int maxwidth = (argc > 3) ? argv[3].toUint16() : 0;
int font_nr = argv[2].toUint16();
+ if (!dest) {
+ debugC(2, kDebugLevelStrings, "GetTextSize: Empty destination");
+ return s->r_acc;
+ }
+
Common::String sep_str;
const char *sep = NULL;
if ((argc > 4) && (argv[4].segment)) {
@@ -356,7 +355,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
dest[0] = dest[1] = NULL_REG;
- if (text.empty() || !dest) { // Empty text
+ if (text.empty()) { // Empty text
dest[2] = dest[3] = make_reg(0, 0);
debugC(2, kDebugLevelStrings, "GetTextSize: Empty string");
return s->r_acc;
@@ -808,6 +807,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:
@@ -903,6 +903,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
@@ -941,7 +945,7 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
}
_k_GenericDrawControl(s, controlObject, false);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kHiliteControl(EngineState *s, int argc, reg_t *argv) {
@@ -1191,6 +1195,57 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
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) {
@@ -1211,69 +1266,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) {
@@ -1300,9 +1324,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];
@@ -1335,75 +1358,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()) {
@@ -1460,6 +1417,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/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index fbe20410de..6d3cd78586 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -180,15 +180,16 @@ enum {
reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
TimeDate loc_time;
- uint32 elapsedTime;
+ uint32 elapsedTime = g_engine->getTotalPlayTime();
int retval = 0; // Avoid spurious warning
g_system->getTimeAndDate(loc_time);
- elapsedTime = g_system->getMillis() - s->gameStartTime;
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) {
@@ -229,14 +230,18 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case K_MEMORY_ALLOCATE_CRITICAL: {
int byteCount = argv[1].toUint16();
- // WORKAROUND: pq3 (multilingual) when plotting crimes - allocates the
- // returned bytes from kStrLen on "W" and "E" and wants to put a
- // string in there, which doesn't fit of course. That's why we allocate
- // one byte more all the time inside that room
- if (g_sci->getGameId() == GID_PQ3) {
- if (s->currentRoomNumber() == 202)
- byteCount++;
- }
+ // WORKAROUND:
+ // - pq3 (multilingual) room 202
+ // when plotting crimes, allocates the returned bytes from kStrLen
+ // on "W" and "E" and wants to put a string in there, which doesn't
+ // fit of course.
+ // - lsl5 (multilingual) room 280
+ // allocates memory according to a previous kStrLen for the name of
+ // the airport ladies (bug #3093818), which isn't enough
+
+ // We always allocate 1 byte more, because of this
+ byteCount++;
+
if (!s->_segMan->allocDynmem(byteCount, "kMemory() critical", &s->r_acc)) {
error("Critical heap allocation failed");
}
@@ -387,6 +392,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/kparse.cpp b/engines/sci/engine/kparse.cpp
index be32b340bb..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);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index faf966af92..cfd455e7b6 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -1311,6 +1311,8 @@ static void AStar(PathfindingState *s) {
}
}
+ assert(vertex_min != 0); // the vertex cost should never be bigger than HUGE_DISTANCE
+
// Check if we are done
if (vertex_min == s->vertex_end)
break;
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index a975ce2988..e7f466f9a2 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -178,6 +178,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
*cloneObj = *parentObj;
// Mark as clone
+ infoSelector &= ~kInfoFlagClass; // remove class bit
writeSelectorValue(s->_segMan, cloneAddr, SELECTOR(_info_), infoSelector | kInfoFlagClone);
cloneObj->setSpeciesSelector(cloneObj->getPos());
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 88f5d29920..4d55febbf9 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -178,24 +178,34 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
// TODO: It would be a good idea to store a magic number & a header size here,
// so that we can implement backward compatibility if the savegame format changes.
- s.syncString(obj.savegame_name);
+ s.syncString(obj.name);
s.syncVersion(CURRENT_SAVEGAME_VERSION);
- obj.savegame_version = s.getVersion();
- s.syncString(obj.game_version);
- s.syncAsSint32LE(obj.savegame_date);
- s.syncAsSint32LE(obj.savegame_time);
+ obj.version = s.getVersion();
+ s.syncString(obj.gameVersion);
+ s.syncAsSint32LE(obj.saveDate);
+ s.syncAsSint32LE(obj.saveTime);
if (s.getVersion() < 22) {
- obj.game_object_offset = 0;
- obj.script0_size = 0;
+ obj.gameObjectOffset = 0;
+ obj.script0Size = 0;
} else {
- s.syncAsUint16LE(obj.game_object_offset);
- s.syncAsUint16LE(obj.script0_size);
+ s.syncAsUint16LE(obj.gameObjectOffset);
+ s.syncAsUint16LE(obj.script0Size);
+ }
+
+ // Playtime
+ obj.playTime = 0;
+ if (s.isLoading()) {
+ if (s.getVersion() >= 26)
+ s.syncAsUint32LE(obj.playTime);
+ } else {
+ obj.playTime = g_engine->getTotalPlayTime() / 1000;
+ s.syncAsUint32LE(obj.playTime);
}
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
Common::String tmp;
- s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version
+ s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be gameVersion
if (getSciVersion() <= SCI_VERSION_1_1) {
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
@@ -520,7 +530,7 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) {
_music->saveLoadWithSerializer(s);
}
-void SoundCommandParser::reconstructPlayList(int savegame_version) {
+void SoundCommandParser::reconstructPlayList(int version) {
Common::StackLock lock(_music->_mutex);
const MusicList::iterator end = _music->getPlayListEnd();
@@ -566,26 +576,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();
+ }
}
}
@@ -675,15 +691,15 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
g_system->getTimeAndDate(curTime);
SavegameMetadata meta;
- meta.savegame_version = CURRENT_SAVEGAME_VERSION;
- meta.savegame_name = savename;
- meta.game_version = version;
- meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+ meta.version = CURRENT_SAVEGAME_VERSION;
+ meta.name = savename;
+ meta.gameVersion = version;
+ meta.saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- meta.script0_size = script0->size;
- meta.game_object_offset = g_sci->getGameObject().offset;
+ meta.script0Size = script0->size;
+ meta.gameObjectOffset = g_sci->getGameObject().offset;
// Checking here again
if (s->executionStackBase) {
@@ -712,13 +728,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if ((meta.savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta.savegame_version > CURRENT_SAVEGAME_VERSION)) {
+ if ((meta.version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta.version > CURRENT_SAVEGAME_VERSION)) {
/*
- if (meta.savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if (meta.version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected, unable to load it");
else
- warning("Savegame version is %d, maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d, maximum supported is %0d", meta.version, CURRENT_SAVEGAME_VERSION);
*/
showScummVMDialog("The format of this saved game is obsolete, unable to load it");
@@ -727,9 +743,9 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if (meta.game_object_offset > 0 && meta.script0_size > 0) {
+ if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- if (script0->size != meta.script0_size || g_sci->getGameObject().offset != meta.game_object_offset) {
+ if (script0->size != meta.script0Size || g_sci->getGameObject().offset != meta.gameObjectOffset) {
//warning("This saved game was created with a different version of the game, unable to load it");
showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
@@ -755,13 +771,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
// Time state:
s->lastWaitTime = g_system->getMillis();
- s->gameStartTime = g_system->getMillis();
s->_screenUpdateTime = g_system->getMillis();
+ g_engine->setTotalPlayTime(meta.playTime * 1000);
if (g_sci->_gfxPorts)
g_sci->_gfxPorts->reset();
- g_sci->_soundCmd->reconstructPlayList(meta.savegame_version);
+ g_sci->_soundCmd->reconstructPlayList(meta.version);
// Message state:
delete s->_msgState;
@@ -783,12 +799,12 @@ bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata
if (stream->eos())
return false;
- if ((meta->savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta->savegame_version > CURRENT_SAVEGAME_VERSION)) {
- if (meta->savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if ((meta->version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta->version > CURRENT_SAVEGAME_VERSION)) {
+ if (meta->version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected- can't load");
else
- warning("Savegame version is %d- maximum supported is %0d", meta->savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d- maximum supported is %0d", meta->version, CURRENT_SAVEGAME_VERSION);
return false;
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index fc254ba33d..7ab28e126b 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,19 +36,20 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 24,
+ CURRENT_SAVEGAME_VERSION = 26,
MINIMUM_SAVEGAME_VERSION = 14
};
// Savegame metadata
struct SavegameMetadata {
- Common::String savegame_name;
- int savegame_version;
- Common::String game_version;
- int savegame_date;
- int savegame_time;
- uint16 game_object_offset;
- uint16 script0_size;
+ Common::String name;
+ int version;
+ Common::String gameVersion;
+ int saveDate;
+ int saveTime;
+ uint32 playTime;
+ uint16 gameObjectOffset;
+ uint16 script0Size;
};
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 9561d337ef..da9ab5106d 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -558,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 810e47b379..7317248021 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -32,20 +32,26 @@
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, NULL, NULL }
+#define SCI_SIGNATUREENTRY_TERMINATOR { 0, NULL, 0, 0, 0, NULL, NULL }
// signatures are built like this:
// - first a counter of the bytes that follow
@@ -55,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
@@ -114,10 +165,10 @@ const uint16 ecoquest1PatchStayAndHelp[] = {
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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 660, "CD: bad messagebox and freeze", 1, PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -176,10 +227,10 @@ const uint16 ecoquest2PatchEcorder[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature ecoquest2Signatures[] = {
- { 50, "initial text not removed on ecorder", PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 50, "initial text not removed on ecorder", 1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -208,6 +259,33 @@ const uint16 freddypharkasPatchScoreDisposal[] = {
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
@@ -242,13 +320,12 @@ const uint16 freddypharkasPatchLadderEvent[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature freddypharkasSignatures[] = {
- { 0, "CD: score early disposal", PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
- // this is not a typo, both lowerLadder::doit and highLadder::doit have the same event code
- { 320, "lower ladder event issue", PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
- { 320, "high ladder event issue", PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 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
};
// ===========================================================================
@@ -317,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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 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
};
// ===========================================================================
@@ -439,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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -490,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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 82, "death dialog memory corruption", 1, PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -521,10 +598,10 @@ 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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 560, "painting closing immediately", 1, PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -563,12 +640,12 @@ const uint16 mothergoose256PatchSaveLimit[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature mothergoose256Signatures[] = {
- { 0, "replay save issue", PATCH_MAGICDWORD(0x20, 0x04, 0xa1, 0xb3), -2, mothergoose256SignatureReplay, mothergoose256PatchReplay },
- { 0, "save limit dialog (SCI1.1)", PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
- { 994, "save limit dialog (SCI1)", PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 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
};
// ===========================================================================
@@ -620,11 +697,11 @@ const uint16 qfg1vgaPatchFightEvents[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature qfg1vgaSignatures[] = {
- { 215, "fight event issue", PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- { 216, "weapon master event issue", PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 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
};
// ===========================================================================
@@ -650,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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -698,26 +775,57 @@ const uint16 sq5PatchScrubbing[] = {
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 },
- SCI_SIGNATUREENTRY_TERMINATOR
+ { 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
- // Can be used to patch in some bytes from the original script into another location
- } 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++;
@@ -766,6 +874,9 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
const SciScriptSignature *signatureTable = NULL;
switch (g_sci->getGameId()) {
+ case GID_CASTLEBRAIN:
+ signatureTable = castlebrainSignatures;
+ break;
case GID_ECOQUEST:
signatureTable = ecoquest1Signatures;
break;
@@ -810,12 +921,17 @@ void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uin
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 7fdd2d25ac..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;
}
@@ -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 6eca708e2e..99a41137e9 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -246,7 +246,7 @@ public:
reg_t getInfoSelector() const { return _variables[_offset + 2]; }
void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; }
- reg_t getNameSelector() const { return _variables[_offset + 3]; }
+ reg_t getNameSelector() const { return _offset + 3 < (uint16)_variables.size() ? _variables[_offset + 3] : NULL_REG; }
void setNameSelector(reg_t value) { _variables[_offset + 3] = value; }
reg_t getPropDictSelector() const { return _variables[2]; }
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index d0ddd5ca06..e5c9334315 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -125,7 +125,6 @@ public:
/* Non-VM information */
- uint32 gameStartTime; /**< The time at which the interpreter was started */
uint32 lastWaitTime; /**< The last time the game invoked Wait() */
uint32 _screenUpdateTime; /**< The last time the game updated the screen */
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index a65c35b69c..ed8d4193a9 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -155,15 +155,6 @@ 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.
@@ -184,8 +175,8 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
// The init selector is always the first function
int initSelectorPos = actorClass->getFuncSelector(0);
- if (names.size() < (uint32)initSelectorPos + 1)
- names.resize((uint32)initSelectorPos + 1);
+ if (names.size() < (uint32)initSelectorPos + 2)
+ names.resize((uint32)initSelectorPos + 2);
names[initSelectorPos] = "init";
// dispose comes right after init
@@ -253,11 +244,12 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
names[275] = "syncCue";
} else if (g_sci->getGameId() == GID_PEPPER) {
// Same as above for the non-interactive demo of Pepper
- if (names.size() < 265)
- names.resize(265);
+ if (names.size() < 539)
+ names.resize(539);
names[263] = "syncTime";
names[264] = "syncCue";
+ names[538] = "startText";
} else if (g_sci->getGameId() == GID_LAURABOW2) {
// The floppy of version needs the changeState selector set to match up with the
// CD version's workarounds.
@@ -265,6 +257,11 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
names.resize(251);
names[144] = "changeState";
+ } else if (g_sci->getGameId() == GID_CNICK_KQ) {
+ if (names.size() < 447)
+ names.resize(447);
+
+ names[446] = "say";
}
#ifdef ENABLE_SCI32
@@ -275,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 b03b1a8f20..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;
@@ -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;
}
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 8e3640c490..d4bab2c671 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -101,12 +101,13 @@ const SciWorkaroundEntry opcodeOrWorkarounds[] = {
// 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, -1, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3 - temps 504 and 505
- { 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)
@@ -165,7 +166,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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", "<noname538>", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
+ { 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
@@ -180,6 +181,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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
@@ -336,6 +338,7 @@ const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
{ GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
{ GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
{ GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 150, 150, 0, "laserScript", "changeState", 0xb2, 0, { WORKAROUND_STILLCALL, 0 } }, // when visiting the pedestral where Roger Jr. is trapped, before trashing the brain icon in the programming chapter, accidental additional parameter specified - bug #3094235
{ GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
{ GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #3036331
{ GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
@@ -366,7 +369,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
};
@@ -409,6 +412,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = {
{ GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference
{ GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473
{ GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time
+ { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room - bug #3098353
{ GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
{ GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
{ GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 75fbb3e097..92bdbd30c3 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -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,7 +191,35 @@ void GfxAnimate::makeSortedList(List *list) {
Common::sort(_list.begin(), _list.end(), sortHelper);
}
-void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
+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) {
reg_t curObject;
uint16 signal;
GfxView *view = NULL;
@@ -238,36 +267,14 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
// 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);
+ applyGlobalScaling(it, view);
}
}
}
- //warning("%s view %d, loop %d, cel %d", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo);
+ //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;
+ bool setNsRect = true;
// Create rect according to coordinates and given cel
if (it->scaleSignal & kScaleSignalDoScaling) {
@@ -276,8 +283,20 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate))
setNsRect = false;
} else {
- view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be
+ // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200)
+ if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) {
+ it->celRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft));
+ it->celRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop));
+ it->celRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight));
+ it->celRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom));
+ view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ setNsRect = false;
+ } else {
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ }
}
+
if (setNsRect) {
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
@@ -533,19 +552,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 +564,33 @@ void GfxAnimate::addToPicDrawCels() {
// Get the corresponding view
view = _cache->getView(it->viewId);
+ // kAddToPic does not do loop/cel-number fixups
+
+ 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) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ 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);
@@ -628,7 +659,7 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
disposeLastCast();
makeSortedList(list);
- fill(old_picNotValid, true);
+ fill(old_picNotValid);
if (old_picNotValid) {
// beginUpdate()/endUpdate() were introduced SCI1.
@@ -704,7 +735,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);
@@ -713,8 +743,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..7b5ec8ee9b 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -51,9 +51,10 @@ 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
+ 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)
+ kScaleSignalHoyle4SpecialHandling = 0x0004 // HOYLE4-exclusive: special handling inside kAnimate, is used when giving out cards
+
};
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 fill(byte &oldPicNotValid, bool maySetNsRect);
+ void applyGlobalScaling(AnimateList::iterator entry, GfxView *view);
+ void fill(byte &oldPicNotValid);
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 1991837102..6a99d2384e 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -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 1abb8478a1..66376a793c 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -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));
@@ -223,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));
@@ -230,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 8e69d034c8..8de9ced57c 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) {
@@ -270,10 +281,10 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// 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 }
+ { 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) {
@@ -329,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;
@@ -353,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() {
@@ -364,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 10cd5d8a85..ae3b51e26a 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -79,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);
@@ -96,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/font.cpp b/engines/sci/graphics/font.cpp
index 852771d081..f06dbea05e 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -88,7 +88,7 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
byte *pIn = getCharData(chr);
for (int i = 0; i < charHeight; i++, y++) {
if (greyedOutput)
- mask = greyedTop++ % 2 ? 0xAA : 0x55;
+ mask = ((greyedTop++) % 2) ? 0xAA : 0x55;
for (int done = 0; done < charWidth; done++) {
if ((done & 7) == 0) // fetching next data byte
b = *(pIn++) & mask;
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index a433b26ef2..b0d1315d2e 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -44,6 +44,8 @@
namespace Sci {
+// TODO/FIXME: This is all guesswork
+
GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette *palette, GfxPaint32 *paint32)
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
@@ -70,6 +72,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,13 +94,63 @@ 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();
+
+ // Update the items in the plane
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
+ if (object == itemPlane) {
+ kernelUpdateScreenItem((*listIterator)->object);
+ }
+ }
+
return;
}
}
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++) {
@@ -144,13 +197,50 @@ void GfxFrameout::deletePlanePictures(reg_t object) {
}
void GfxFrameout::kernelAddScreenItem(reg_t object) {
- _screenItems.push_back(object);
+ // Ignore invalid items
+ if (!_segMan->isObject(object))
+ return;
+
+ FrameoutEntry *itemEntry = new FrameoutEntry();
+ itemEntry->object = object;
+ itemEntry->givenOrderNr = _screenItems.size();
+ _screenItems.push_back(itemEntry);
+
+ kernelUpdateScreenItem(object);
+}
+
+void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
+ // Ignore invalid items
+ if (!_segMan->isObject(object))
+ return;
+
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *itemEntry = *listIterator;
+
+ if (itemEntry->object == object) {
+ itemEntry->viewId = readSelectorValue(_segMan, object, SELECTOR(view));
+ itemEntry->loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
+ itemEntry->celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
+ itemEntry->x = readSelectorValue(_segMan, object, SELECTOR(x));
+ itemEntry->y = readSelectorValue(_segMan, object, SELECTOR(y));
+ itemEntry->z = readSelectorValue(_segMan, object, SELECTOR(z));
+ itemEntry->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
+ if (readSelectorValue(_segMan, object, SELECTOR(fixPriority)) == 0)
+ itemEntry->priority = itemEntry->y;
+
+ itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
+ itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
+ itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
+ return;
+ }
+ }
}
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
- for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- if (_screenItems[itemNr] == object) {
- _screenItems.remove_at(itemNr);
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *itemEntry = *listIterator;
+ if (itemEntry->object == object) {
+ _screenItems.remove(itemEntry);
return;
}
}
@@ -161,7 +251,7 @@ int16 GfxFrameout::kernelGetHighPlanePri() {
return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
}
-// No idea yet how to implement this
+// TODO: No idea yet how to implement this
void GfxFrameout::kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId) {
addPlanePicture(planeObj, pictureId, forWidth);
}
@@ -206,112 +296,37 @@ void GfxFrameout::sortPlanes() {
void GfxFrameout::kernelFrameout() {
_palette->palVaryUpdate();
- // Allocate enough space for all screen items
- 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
- int16 itemCount = 0;
- FrameoutEntry *itemEntry = itemData;
FrameoutList itemList;
- for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- reg_t itemObject = _screenItems[itemNr];
-
- // Remove any invalid items
- if (!_segMan->isObject(itemObject)) {
- _screenItems.remove_at(itemNr);
- itemNr--;
- continue;
- }
-
- reg_t itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
+ // Copy screen items of the current frame to the list of items to be drawn
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
if (planeObject == itemPlane) {
- // Found an item on current plane
- itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view));
- itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop));
- itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel));
- itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x));
- itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y));
- itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z));
- itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority));
- if (readSelectorValue(_segMan, itemObject, SELECTOR(fixPriority)) == 0)
- itemEntry->priority = itemEntry->y;
-
- itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal));
- itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX));
- itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY));
- itemEntry->object = itemObject;
- itemEntry->givenOrderNr = itemNr;
-
- itemList.push_back(itemEntry);
- itemEntry++;
- itemCount++;
+ kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary?
+ itemList.push_back(*listIterator);
}
}
@@ -343,13 +358,10 @@ void GfxFrameout::kernelFrameout() {
// Now sort our itemlist
Common::sort(itemList.begin(), itemList.end(), sortHelper);
- // Now display itemlist
- itemEntry = itemData;
-
// warning("Plane %s", _segMan->getObjectName(planeObject));
for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
- itemEntry = *listIterator;
+ FrameoutEntry *itemEntry = *listIterator;
if (itemEntry->object.isNull()) {
// Picture cel data
@@ -360,25 +372,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 +415,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 +438,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 +477,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 +513,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
@@ -525,7 +537,6 @@ void GfxFrameout::kernelFrameout() {
}
}
- delete[] itemData;
_screen->copyToScreen();
g_sci->getEngineState()->_throttleTrigger = true;
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index f8f7e54a27..bd708dbc79 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);
@@ -100,7 +109,7 @@ private:
GfxScreen *_screen;
GfxPaint32 *_paint32;
- Common::Array<reg_t> _screenItems;
+ Common::List<FrameoutEntry *> _screenItems;
PlaneList _planes;
PlanePictureList _planePictures;
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/picture.cpp b/engines/sci/graphics/picture.cpp
index 2765663381..39666b82cb 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -853,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/ports.cpp b/engines/sci/graphics/ports.cpp
index 12ac1d2025..e7f319a25c 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -301,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;
}
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..7a10a6b749 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -122,9 +122,9 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
// Initialize the actual screen
- if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) {
- // For SCI1.1 Mac, we need to expand the screen to accommodate for
- // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
+ if (g_sci->hasMacIconBar()) {
+ // For SCI1.1 Mac games with the custom icon bar, we need to expand the screen
+ // to accommodate for the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
if (g_sci->getGameId() == GID_KQ6)
initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320);
else if (g_sci->getGameId() == GID_QFG1VGA)
@@ -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 bd5e061094..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,7 +118,11 @@ 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;
}
}
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 6b22ed397e..36d48fe3c9 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -354,6 +354,16 @@ void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, C
outRect.top = outRect.bottom - celInfo->height;
}
+void GfxView::getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ int16 adjustY = y - celInfo->height + celInfo->displaceY + 1;
+ int16 adjustX = x - ((celInfo->width - 1) >> 1) + celInfo->displaceX;
+ outRect.top += adjustY;
+ outRect.bottom += adjustY;
+ outRect.left += adjustX;
+ outRect.right += adjustX;
+}
+
void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
int16 scaledDisplaceX, scaledDisplaceY;
int16 scaledWidth, scaledHeight;
@@ -525,6 +535,10 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
if (width <= 3)
return;
+ // We need at least 2 pixel lines
+ if (height < 2)
+ return;
+
// If EGA mapping is used for this view, dont do undithering as well
if (_EGAmapping)
return;
@@ -533,20 +547,28 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
byte *curPtr;
byte color1, color2;
+ byte nextColor1, nextColor2;
int16 y, x;
memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
// Count all seemingly dithered pixel-combinations as soon as at least 4
- // pixels are adjacent
+ // pixels are adjacent and check pixels in the following line as well to
+ // be the reverse pixel combination
+ int16 checkHeight = height - 1;
curPtr = bitmapPtr;
- for (y = 0; y < height; y++) {
+ byte *nextPtr = curPtr + width;
+ for (y = 0; y < checkHeight; y++) {
color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
+ nextColor1 = nextPtr[0] << 4; nextColor2 = (nextPtr[2] << 4) | nextPtr[1];
curPtr += 3;
+ nextPtr += 3;
for (x = 3; x < width; x++) {
color1 = (color1 << 4) | (color2 >> 4);
color2 = (color2 << 4) | *curPtr++;
- if (color1 == color2)
+ nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4);
+ nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4;
+ if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2))
bitmapMemorial[color1]++;
}
}
@@ -583,9 +605,10 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
if (unditherTable[color]) {
// Some color with black? Turn colors around, otherwise it won't
// be the right color at all.
+ byte unditheredColor = color;
if ((color & 0xF0) == 0)
- color = (color << 4) | (color >> 4);
- curPtr[0] = color; curPtr[1] = color;
+ unditheredColor = (color << 4) | (color >> 4);
+ curPtr[0] = unditheredColor; curPtr[1] = unditheredColor;
}
curPtr++;
}
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 990a7e2f71..f785e3c475 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -66,6 +66,7 @@ public:
int16 getHeight(int16 loopNo, int16 celNo) const;
const CelInfo *getCelInfo(int16 loopNo, int16 celNo) const;
void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
+ void getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const;
const byte *getBitmap(int16 loopNo, int16 celNo);
void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
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 46fb0fbf99..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
}
@@ -1945,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
@@ -1966,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) {
@@ -2353,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 53c00f6ec0..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.
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 590926dbbd..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);
@@ -664,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 8fc0f667d3..f8d25efe73 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -303,17 +303,17 @@ Common::Error SciEngine::run() {
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");
+ "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");
}
}
// 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) {
+ if (_soundCmd->getMusicType() == MT_GM && !ConfMan.getBool("native_mt32")) {
if (!_resMan->findResource(ResourceId(kResourceTypePatch, 4), 0)) {
switch (getGameId()) {
case GID_ECOQUEST:
@@ -325,16 +325,16 @@ Common::Error SciEngine::run() {
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.");
+ "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;
@@ -342,6 +342,13 @@ Common::Error SciEngine::run() {
}
}
+ if (gameHasFanMadePatch()) {
+ showScummVMDialog("Your game is patched with a fan made script patch. Such patches have "
+ "been reported to cause issues, as they modify game scripts extensively. "
+ "The issues that these patches fix do not occur in ScummVM, so you are "
+ "advised to remove this patch from your game folder in order to avoid "
+ "having unexpected errors and/or issues later on.");
+ }
runGame();
@@ -350,6 +357,69 @@ Common::Error SciEngine::run() {
return Common::kNoError;
}
+bool SciEngine::gameHasFanMadePatch() {
+ struct FanMadePatchInfo {
+ SciGameId gameID;
+ uint16 targetScript;
+ uint16 targetSize;
+ uint16 patchedByteOffset;
+ byte patchedByte;
+ };
+
+ const FanMadePatchInfo patchInfo[] = {
+ // game script size offset byte
+ // ** NRS Patches **************************
+ { GID_HOYLE3, 994, 2580, 656, 0x78 },
+ { GID_KQ1, 85, 5156, 631, 0x02 },
+ { GID_LAURABOW2, 994, 4382, 0, 0x00 },
+ { GID_LONGBOW, 994, 4950, 1455, 0x78 }, // English
+ { GID_LONGBOW, 994, 5020, 1469, 0x78 }, // German
+ { GID_LSL1, 803, 592, 342, 0x01 },
+ { GID_LSL3, 380, 6148, 195, 0x35 },
+ { GID_LSL5, 994, 4810, 1342, 0x78 }, // English
+ { GID_LSL5, 994, 4942, 1392, 0x76 }, // German
+ { GID_PQ1, 994, 4332, 1473, 0x78 },
+ { GID_PQ2, 200, 10614, 0, 0x00 },
+ { GID_PQ3, 994, 4686, 1291, 0x78 }, // English
+ { GID_PQ3, 994, 4734, 1283, 0x78 }, // German
+ { GID_QFG1VGA, 994, 4388, 0, 0x00 },
+ { GID_QFG3, 994, 4714, 0, 0x00 },
+ // TODO: Disabled, as it fixes a whole lot of bugs which can't be tested till SCI2.1 support is finished
+ //{ GID_QFG4, 710, 11477, 0, 0x00 },
+ { GID_SQ1, 994, 4740, 0, 0x00 },
+ { GID_SQ5, 994, 4142, 1496, 0x78 }, // English/German/French
+ // TODO: Disabled, till we can test the Italian version
+ //{ GID_SQ5, 994, 4148, 0, 0x00 }, // Italian - patched file is the same size as the original
+ // TODO: The bugs in SQ6 can't be tested till SCI2.1 support is finished
+ //{ GID_SQ6, 380, 16308, 15042, 0x0C }, // English
+ //{ GID_SQ6, 380, 11652, 0, 0x00 }, // German - patched file is the same size as the original
+ // ** End marker ***************************
+ { GID_FANMADE, 0, 0, 0, 0x00 }
+ };
+
+ int curEntry = 0;
+
+ while (true) {
+ if (patchInfo[curEntry].targetSize == 0)
+ break;
+
+ if (patchInfo[curEntry].gameID == getGameId()) {
+ Resource *targetScript = _resMan->findResource(ResourceId(kResourceTypeScript, patchInfo[curEntry].targetScript), 0);
+
+ if (targetScript && targetScript->size + 2 == patchInfo[curEntry].targetSize) {
+ if (patchInfo[curEntry].patchedByteOffset == 0)
+ return true;
+ else if (targetScript->data[patchInfo[curEntry].patchedByteOffset - 2] == patchInfo[curEntry].patchedByte)
+ return true;
+ }
+ }
+
+ curEntry++;
+ }
+
+ return false;
+}
+
static byte patchGameRestoreSave[] = {
0x39, 0x03, // pushi 03
0x76, // push0
@@ -377,7 +447,9 @@ void SciEngine::patchGameSaveRestore(SegManager *segMan) {
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, although the game isn't able to save/restore o_O
+ 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;
@@ -482,7 +554,7 @@ bool SciEngine::initGame() {
_vocabulary->reset();
}
- _gamestate->gameStartTime = _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
+ _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
// Load game language into printLang property of game object
setSciLanguage();
@@ -512,7 +584,7 @@ void SciEngine::initGraphics() {
_gfxPaint32 = 0;
#endif
- if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1)
+ if (hasMacIconBar())
_gfxMacIconBar = new GfxMacIconBar();
bool paletteMerging = true;
@@ -578,6 +650,8 @@ void SciEngine::initStackBaseWithSelector(Selector selector) {
}
void SciEngine::runGame() {
+ setTotalPlayTime(0);
+
initStackBaseWithSelector(SELECTOR(play)); // Call the play selector
// Attach the debug console on game startup, if requested
@@ -631,8 +705,10 @@ void SciEngine::exitGame() {
GUI::Debugger *SciEngine::getDebugger() {
if (_gamestate) {
ExecStack *xs = &(_gamestate->_executionStack.back());
- xs->addr.pc.offset = _debugState.old_pc_offset;
- xs->sp = _debugState.old_sp;
+ if (xs) {
+ xs->addr.pc.offset = _debugState.old_pc_offset;
+ xs->sp = _debugState.old_sp;
+ }
}
_debugState.runningStep = 0; // Stop multiple execution
@@ -662,6 +738,10 @@ bool SciEngine::isDemo() const {
return _gameDescription->flags & ADGF_DEMO;
}
+bool SciEngine::hasMacIconBar() const {
+ return _resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1 && getGameId() != GID_HOYLE4;
+}
+
Common::String SciEngine::getSavegameName(int nr) const {
return _targetName + Common::String::printf(".%03d", nr);
}
@@ -700,6 +780,8 @@ int SciEngine::inQfGImportRoom() const {
void SciEngine::pauseEngineIntern(bool pause) {
_mixer->pauseAll(pause);
+ if (_soundCmd)
+ _soundCmd->pauseAll(pause);
}
void SciEngine::syncSoundSettings() {
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 7239abad17..606cc008ee 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -39,8 +39,11 @@ struct ADGameDescription;
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Newer Sierra adventure games (based on FreeSCI)
+ *
+ * @todo give a concrete list of supported games. Could also
+ * list future games, with status for each.
*/
namespace Sci {
@@ -228,6 +231,7 @@ public:
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
bool isDemo() const;
+ bool hasMacIconBar() const;
inline ResourceManager *getResMan() const { return _resMan; }
inline Kernel *getKernel() const { return _kernel; }
@@ -344,6 +348,8 @@ private:
void initStackBaseWithSelector(Selector selector);
+ bool gameHasFanMadePatch();
+
const ADGameDescription *_gameDescription;
const SciGameId _gameId;
ResourceManager *_resMan; /**< The resource manager */
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/gm_names.h b/engines/sci/sound/drivers/gm_names.h
new file mode 100644
index 0000000000..b7883494f6
--- /dev/null
+++ b/engines/sci/sound/drivers/gm_names.h
@@ -0,0 +1,220 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_SOUND_DRIVERS_GM_NAMES_H
+#define SCI_SOUND_DRIVERS_GM_NAMES_H
+
+namespace Sci {
+
+static const char *GmInstrumentNames[] = {
+ /*000*/ "Acoustic Grand Piano",
+ /*001*/ "Bright Acoustic Piano",
+ /*002*/ "Electric Grand Piano",
+ /*003*/ "Honky-tonk Piano",
+ /*004*/ "Electric Piano 1",
+ /*005*/ "Electric Piano 2",
+ /*006*/ "Harpsichord",
+ /*007*/ "Clavinet",
+ /*008*/ "Celesta",
+ /*009*/ "Glockenspiel",
+ /*010*/ "Music Box",
+ /*011*/ "Vibraphone",
+ /*012*/ "Marimba",
+ /*013*/ "Xylophone",
+ /*014*/ "Tubular Bells",
+ /*015*/ "Dulcimer",
+ /*016*/ "Drawbar Organ",
+ /*017*/ "Percussive Organ",
+ /*018*/ "Rock Organ",
+ /*019*/ "Church Organ",
+ /*020*/ "Reed Organ",
+ /*021*/ "Accordion",
+ /*022*/ "Harmonica",
+ /*023*/ "Tango Accordion",
+ /*024*/ "Acoustic Guitar (nylon)",
+ /*025*/ "Acoustic Guitar (steel)",
+ /*026*/ "Electric Guitar (jazz)",
+ /*027*/ "Electric Guitar (clean)",
+ /*028*/ "Electric Guitar (muted)",
+ /*029*/ "Overdriven Guitar",
+ /*030*/ "Distortion Guitar",
+ /*031*/ "Guitar Harmonics",
+ /*032*/ "Acoustic Bass",
+ /*033*/ "Electric Bass (finger)",
+ /*034*/ "Electric Bass (pick)",
+ /*035*/ "Fretless Bass",
+ /*036*/ "Slap Bass 1",
+ /*037*/ "Slap Bass 2",
+ /*038*/ "Synth Bass 1",
+ /*039*/ "Synth Bass 2",
+ /*040*/ "Violin",
+ /*041*/ "Viola",
+ /*042*/ "Cello",
+ /*043*/ "Contrabass",
+ /*044*/ "Tremolo Strings",
+ /*045*/ "Pizzicato Strings",
+ /*046*/ "Orchestral Harp",
+ /*047*/ "Timpani",
+ /*048*/ "String Ensemble 1",
+ /*049*/ "String Ensemble 2",
+ /*050*/ "SynthStrings 1",
+ /*051*/ "SynthStrings 2",
+ /*052*/ "Choir Aahs",
+ /*053*/ "Voice Oohs",
+ /*054*/ "Synth Voice",
+ /*055*/ "Orchestra Hit",
+ /*056*/ "Trumpet",
+ /*057*/ "Trombone",
+ /*058*/ "Tuba",
+ /*059*/ "Muted Trumpet",
+ /*060*/ "French Horn",
+ /*061*/ "Brass Section",
+ /*062*/ "SynthBrass 1",
+ /*063*/ "SynthBrass 2",
+ /*064*/ "Soprano Sax",
+ /*065*/ "Alto Sax",
+ /*066*/ "Tenor Sax",
+ /*067*/ "Baritone Sax",
+ /*068*/ "Oboe",
+ /*069*/ "English Horn",
+ /*070*/ "Bassoon",
+ /*071*/ "Clarinet",
+ /*072*/ "Piccolo",
+ /*073*/ "Flute",
+ /*074*/ "Recorder",
+ /*075*/ "Pan Flute",
+ /*076*/ "Blown Bottle",
+ /*077*/ "Shakuhachi",
+ /*078*/ "Whistle",
+ /*079*/ "Ocarina",
+ /*080*/ "Lead 1 (square)",
+ /*081*/ "Lead 2 (sawtooth)",
+ /*082*/ "Lead 3 (calliope)",
+ /*083*/ "Lead 4 (chiff)",
+ /*084*/ "Lead 5 (charang)",
+ /*085*/ "Lead 6 (voice)",
+ /*086*/ "Lead 7 (fifths)",
+ /*087*/ "Lead 8 (bass+lead)",
+ /*088*/ "Pad 1 (new age)",
+ /*089*/ "Pad 2 (warm)",
+ /*090*/ "Pad 3 (polysynth)",
+ /*091*/ "Pad 4 (choir)",
+ /*092*/ "Pad 5 (bowed)",
+ /*093*/ "Pad 6 (metallic)",
+ /*094*/ "Pad 7 (halo)",
+ /*095*/ "Pad 8 (sweep)",
+ /*096*/ "FX 1 (rain)",
+ /*097*/ "FX 2 (soundtrack)",
+ /*098*/ "FX 3 (crystal)",
+ /*099*/ "FX 4 (atmosphere)",
+ /*100*/ "FX 5 (brightness)",
+ /*101*/ "FX 6 (goblins)",
+ /*102*/ "FX 7 (echoes)",
+ /*103*/ "FX 8 (sci-fi)",
+ /*104*/ "Sitar",
+ /*105*/ "Banjo",
+ /*106*/ "Shamisen",
+ /*107*/ "Koto",
+ /*108*/ "Kalimba",
+ /*109*/ "Bag pipe",
+ /*110*/ "Fiddle",
+ /*111*/ "Shannai",
+ /*112*/ "Tinkle Bell",
+ /*113*/ "Agogo",
+ /*114*/ "Steel Drums",
+ /*115*/ "Woodblock",
+ /*116*/ "Taiko Drum",
+ /*117*/ "Melodic Tom",
+ /*118*/ "Synth Drum",
+ /*119*/ "Reverse Cymbal",
+ /*120*/ "Guitar Fret Noise",
+ /*121*/ "Breath Noise",
+ /*122*/ "Seashore",
+ /*123*/ "Bird Tweet",
+ /*124*/ "Telephone Ring",
+ /*125*/ "Helicopter",
+ /*126*/ "Applause",
+ /*127*/ "Gunshot"
+};
+
+// The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI
+static const char *GmPercussionNames[] = {
+ /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*30*/ 0, 0, 0, 0, 0,
+ // The preceeding percussions are not covered by the GM standard
+ /*35*/ "Acoustic Bass Drum",
+ /*36*/ "Bass Drum 1",
+ /*37*/ "Side Stick",
+ /*38*/ "Acoustic Snare",
+ /*39*/ "Hand Clap",
+ /*40*/ "Electric Snare",
+ /*41*/ "Low Floor Tom",
+ /*42*/ "Closed Hi-Hat",
+ /*43*/ "High Floor Tom",
+ /*44*/ "Pedal Hi-Hat",
+ /*45*/ "Low Tom",
+ /*46*/ "Open Hi-Hat",
+ /*47*/ "Low-Mid Tom",
+ /*48*/ "Hi-Mid Tom",
+ /*49*/ "Crash Cymbal 1",
+ /*50*/ "High Tom",
+ /*51*/ "Ride Cymbal 1",
+ /*52*/ "Chinese Cymbal",
+ /*53*/ "Ride Bell",
+ /*54*/ "Tambourine",
+ /*55*/ "Splash Cymbal",
+ /*56*/ "Cowbell",
+ /*57*/ "Crash Cymbal 2",
+ /*58*/ "Vibraslap",
+ /*59*/ "Ride Cymbal 2",
+ /*60*/ "Hi Bongo",
+ /*61*/ "Low Bongo",
+ /*62*/ "Mute Hi Conga",
+ /*63*/ "Open Hi Conga",
+ /*64*/ "Low Conga",
+ /*65*/ "High Timbale",
+ /*66*/ "Low Timbale",
+ /*67*/ "High Agogo",
+ /*68*/ "Low Agogo",
+ /*69*/ "Cabasa",
+ /*70*/ "Maracas",
+ /*71*/ "Short Whistle",
+ /*72*/ "Long Whistle",
+ /*73*/ "Short Guiro",
+ /*74*/ "Long Guiro",
+ /*75*/ "Claves",
+ /*76*/ "Hi Wood Block",
+ /*77*/ "Low Wood Block",
+ /*78*/ "Mute Cuica",
+ /*79*/ "Open Cuica",
+ /*80*/ "Mute Triangle",
+ /*81*/ "Open Triangle"
+};
+
+} // End of namespace Sci
+
+#endif // SCI_SOUND_DRIVERS_GM_NAMES_H
diff --git a/engines/sci/sound/drivers/map-mt32-to-gm.h b/engines/sci/sound/drivers/map-mt32-to-gm.h
index 05d1aeba24..f7a6256ba4 100644
--- a/engines/sci/sound/drivers/map-mt32-to-gm.h
+++ b/engines/sci/sound/drivers/map-mt32-to-gm.h
@@ -23,11 +23,16 @@
*
*/
+#ifndef SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
+#define SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
+
namespace Sci {
-/* Patch not mapped */
+#include "common/list.h"
+
+// Patch not mapped
#define MIDI_UNMAPPED 0xff
-/* Patch mapped to rhythm key */
+// Patch mapped to rhythm key
#define MIDI_MAPPED_TO_RHYTHM 0xfe
struct Mt32ToGmMap {
@@ -36,193 +41,6 @@ struct Mt32ToGmMap {
uint8 gmRhythmKey;
};
-static const char *GmInstrumentNames[] = {
- /*000*/ "Acoustic Grand Piano",
- /*001*/ "Bright Acoustic Piano",
- /*002*/ "Electric Grand Piano",
- /*003*/ "Honky-tonk Piano",
- /*004*/ "Electric Piano 1",
- /*005*/ "Electric Piano 2",
- /*006*/ "Harpsichord",
- /*007*/ "Clavinet",
- /*008*/ "Celesta",
- /*009*/ "Glockenspiel",
- /*010*/ "Music Box",
- /*011*/ "Vibraphone",
- /*012*/ "Marimba",
- /*013*/ "Xylophone",
- /*014*/ "Tubular Bells",
- /*015*/ "Dulcimer",
- /*016*/ "Drawbar Organ",
- /*017*/ "Percussive Organ",
- /*018*/ "Rock Organ",
- /*019*/ "Church Organ",
- /*020*/ "Reed Organ",
- /*021*/ "Accordion",
- /*022*/ "Harmonica",
- /*023*/ "Tango Accordion",
- /*024*/ "Acoustic Guitar (nylon)",
- /*025*/ "Acoustic Guitar (steel)",
- /*026*/ "Electric Guitar (jazz)",
- /*027*/ "Electric Guitar (clean)",
- /*028*/ "Electric Guitar (muted)",
- /*029*/ "Overdriven Guitar",
- /*030*/ "Distortion Guitar",
- /*031*/ "Guitar Harmonics",
- /*032*/ "Acoustic Bass",
- /*033*/ "Electric Bass (finger)",
- /*034*/ "Electric Bass (pick)",
- /*035*/ "Fretless Bass",
- /*036*/ "Slap Bass 1",
- /*037*/ "Slap Bass 2",
- /*038*/ "Synth Bass 1",
- /*039*/ "Synth Bass 2",
- /*040*/ "Violin",
- /*041*/ "Viola",
- /*042*/ "Cello",
- /*043*/ "Contrabass",
- /*044*/ "Tremolo Strings",
- /*045*/ "Pizzicato Strings",
- /*046*/ "Orchestral Harp",
- /*047*/ "Timpani",
- /*048*/ "String Ensemble 1",
- /*049*/ "String Ensemble 2",
- /*050*/ "SynthStrings 1",
- /*051*/ "SynthStrings 2",
- /*052*/ "Choir Aahs",
- /*053*/ "Voice Oohs",
- /*054*/ "Synth Voice",
- /*055*/ "Orchestra Hit",
- /*056*/ "Trumpet",
- /*057*/ "Trombone",
- /*058*/ "Tuba",
- /*059*/ "Muted Trumpet",
- /*060*/ "French Horn",
- /*061*/ "Brass Section",
- /*062*/ "SynthBrass 1",
- /*063*/ "SynthBrass 2",
- /*064*/ "Soprano Sax",
- /*065*/ "Alto Sax",
- /*066*/ "Tenor Sax",
- /*067*/ "Baritone Sax",
- /*068*/ "Oboe",
- /*069*/ "English Horn",
- /*070*/ "Bassoon",
- /*071*/ "Clarinet",
- /*072*/ "Piccolo",
- /*073*/ "Flute",
- /*074*/ "Recorder",
- /*075*/ "Pan Flute",
- /*076*/ "Blown Bottle",
- /*077*/ "Shakuhachi",
- /*078*/ "Whistle",
- /*079*/ "Ocarina",
- /*080*/ "Lead 1 (square)",
- /*081*/ "Lead 2 (sawtooth)",
- /*082*/ "Lead 3 (calliope)",
- /*083*/ "Lead 4 (chiff)",
- /*084*/ "Lead 5 (charang)",
- /*085*/ "Lead 6 (voice)",
- /*086*/ "Lead 7 (fifths)",
- /*087*/ "Lead 8 (bass+lead)",
- /*088*/ "Pad 1 (new age)",
- /*089*/ "Pad 2 (warm)",
- /*090*/ "Pad 3 (polysynth)",
- /*091*/ "Pad 4 (choir)",
- /*092*/ "Pad 5 (bowed)",
- /*093*/ "Pad 6 (metallic)",
- /*094*/ "Pad 7 (halo)",
- /*095*/ "Pad 8 (sweep)",
- /*096*/ "FX 1 (rain)",
- /*097*/ "FX 2 (soundtrack)",
- /*098*/ "FX 3 (crystal)",
- /*099*/ "FX 4 (atmosphere)",
- /*100*/ "FX 5 (brightness)",
- /*101*/ "FX 6 (goblins)",
- /*102*/ "FX 7 (echoes)",
- /*103*/ "FX 8 (sci-fi)",
- /*104*/ "Sitar",
- /*105*/ "Banjo",
- /*106*/ "Shamisen",
- /*107*/ "Koto",
- /*108*/ "Kalimba",
- /*109*/ "Bag pipe",
- /*110*/ "Fiddle",
- /*111*/ "Shannai",
- /*112*/ "Tinkle Bell",
- /*113*/ "Agogo",
- /*114*/ "Steel Drums",
- /*115*/ "Woodblock",
- /*116*/ "Taiko Drum",
- /*117*/ "Melodic Tom",
- /*118*/ "Synth Drum",
- /*119*/ "Reverse Cymbal",
- /*120*/ "Guitar Fret Noise",
- /*121*/ "Breath Noise",
- /*122*/ "Seashore",
- /*123*/ "Bird Tweet",
- /*124*/ "Telephone Ring",
- /*125*/ "Helicopter",
- /*126*/ "Applause",
- /*127*/ "Gunshot"
-};
-
-/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */
-static const char *GmPercussionNames[] = {
- /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- /*30*/ 0, 0, 0, 0, 0,
- /* The preceeding percussions are not covered by the GM standard */
- /*35*/ "Acoustic Bass Drum",
- /*36*/ "Bass Drum 1",
- /*37*/ "Side Stick",
- /*38*/ "Acoustic Snare",
- /*39*/ "Hand Clap",
- /*40*/ "Electric Snare",
- /*41*/ "Low Floor Tom",
- /*42*/ "Closed Hi-Hat",
- /*43*/ "High Floor Tom",
- /*44*/ "Pedal Hi-Hat",
- /*45*/ "Low Tom",
- /*46*/ "Open Hi-Hat",
- /*47*/ "Low-Mid Tom",
- /*48*/ "Hi-Mid Tom",
- /*49*/ "Crash Cymbal 1",
- /*50*/ "High Tom",
- /*51*/ "Ride Cymbal 1",
- /*52*/ "Chinese Cymbal",
- /*53*/ "Ride Bell",
- /*54*/ "Tambourine",
- /*55*/ "Splash Cymbal",
- /*56*/ "Cowbell",
- /*57*/ "Crash Cymbal 2",
- /*58*/ "Vibraslap",
- /*59*/ "Ride Cymbal 2",
- /*60*/ "Hi Bongo",
- /*61*/ "Low Bongo",
- /*62*/ "Mute Hi Conga",
- /*63*/ "Open Hi Conga",
- /*64*/ "Low Conga",
- /*65*/ "High Timbale",
- /*66*/ "Low Timbale",
- /*67*/ "High Agogo",
- /*68*/ "Low Agogo",
- /*69*/ "Cabasa",
- /*70*/ "Maracas",
- /*71*/ "Short Whistle",
- /*72*/ "Long Whistle",
- /*73*/ "Short Guiro",
- /*74*/ "Long Guiro",
- /*75*/ "Claves",
- /*76*/ "Hi Wood Block",
- /*77*/ "Low Wood Block",
- /*78*/ "Mute Cuica",
- /*79*/ "Open Cuica",
- /*80*/ "Mute Triangle",
- /*81*/ "Open Triangle"
-};
-
/*******************************************
* Fancy instrument mappings begin here... *
*******************************************/
@@ -344,19 +162,19 @@ static const Mt32ToGmMap Mt32PresetTimbreMaps[] = {
/*112*/ {"Timpani ", 47, MIDI_UNMAPPED},
/*113*/ {"MelodicTom", 117, MIDI_UNMAPPED},
/*114*/ {"Deep Snare", MIDI_MAPPED_TO_RHYTHM, 38},
- /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, /* ? */
- /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, /* ? */
+ /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, // ?
+ /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, // ?
/*117*/ {"Taiko ", 116, MIDI_UNMAPPED},
/*118*/ {"Taiko Rim ", 118, MIDI_UNMAPPED},
/*119*/ {"Cymbal ", MIDI_MAPPED_TO_RHYTHM, 51},
- /*120*/ {"Castanets ", MIDI_UNMAPPED, MIDI_UNMAPPED},
+ /*120*/ {"Castanets ", MIDI_MAPPED_TO_RHYTHM, 75}, // approximation
/*121*/ {"Triangle ", 112, MIDI_UNMAPPED},
/*122*/ {"Orche Hit ", 55, MIDI_UNMAPPED},
/*123*/ {"Telephone ", 124, MIDI_UNMAPPED},
/*124*/ {"Bird Tweet", 123, MIDI_UNMAPPED},
- /*125*/ {"OneNoteJam", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? */
+ /*125*/ {"OneNoteJam", 8, MIDI_UNMAPPED}, // approximation
/*126*/ {"WaterBells", 98, MIDI_UNMAPPED},
- /*127*/ {"JungleTune", MIDI_UNMAPPED, MIDI_UNMAPPED} /* ? */
+ /*127*/ {"JungleTune", 75, MIDI_UNMAPPED} // approximation
};
static const Mt32ToGmMap Mt32RhythmTimbreMaps[] = {
@@ -414,139 +232,146 @@ static const uint8 Mt32PresetRhythmKeymap[] = {
? - Where do I map this one?
?? - Any good ideas?
??? - I'm clueless?
- R - Rhythm... */
+ R - Rhythm...
+*/
static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = {
- {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 35}, /* R (PQ2) */
- {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (PQ2) */
- {"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */
- {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (LB1) */
- {"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */
+ {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 35}, // R (PQ2)
+ {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, // R (PQ2)
+ {"AcouPnoKA ", 0, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"BASS ", 32, MIDI_UNMAPPED}, // + (LSL3)
+ {"BASSOONPCM", 70, MIDI_UNMAPPED}, // + (LB1)
+ {"BEACH WAVE", 122, MIDI_UNMAPPED}, // + (LSL3)
{"BagPipes ", 109, MIDI_UNMAPPED},
- {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (QFG1) */
- {"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (Iceman) */
- {"Bells MS", 112, MIDI_UNMAPPED}, /* + (QFG1) */
- {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (LB1) */
+ {"BassPizzMS", 45, MIDI_UNMAPPED}, // ++ (QFG1)
+ {"BassoonKA ", 70, MIDI_UNMAPPED}, // ++ (KQ1)
+ {"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) */
- {"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (Hoyle) */
- {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (QFG1) */
- {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (Camelot) */
- {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (Iceman) */
+ {"BrsSect MS", 61, MIDI_UNMAPPED}, // +++ (Iceman)
+ {"CLAPPING ", 126, MIDI_UNMAPPED}, // ++ (LSL3)
+ {"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}, /* + (Camelot) */
+ {"Chorale MS", 52, MIDI_UNMAPPED}, // + (Camelot)
{"ClarinetMS", 71, MIDI_UNMAPPED},
- {"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, /* R (PQ2) */
- {"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 (QFG1) */
- {"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
- {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
- {"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}, /* ? (Hoyle) */
- {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (Iceman) */
- {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (Iceman) */
- {"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */
+ {"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, // R (PQ2)
+ {"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 (QFG1)
+ {"CoolPhone ", 124, MIDI_UNMAPPED}, // ++ (LSL3)
+ {"CracklesMS", 115, MIDI_UNMAPPED}, // ? (Camelot, QFG1)
+ {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ??? (KQ1)
+ {"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}, // ? (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}, /* ++ (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) */
+ {"Fantasy ", 99, MIDI_UNMAPPED}, // + (PQ2)
+ {"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}, /* +++ (QFG1) */
- {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (LB1) */
+ {"FrHorn1 MS", 60, MIDI_UNMAPPED}, // +++ (QFG1)
+ {"FunnyTrmp ", 56, MIDI_UNMAPPED}, // ++ (LB1)
{"GameSnd MS", 80, MIDI_UNMAPPED},
- {"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}, /* +++ (LB1) */
- {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (QFG1) */
- {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
- {"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */
- {"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}, /* ? (LB1) */
- {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (Iceman) */
- {"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */
+ {"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}, // +++ (LB1)
+ {"Kiss MS", 25, MIDI_UNMAPPED}, // ++ (QFG1)
+ {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ??? (KQ1)
+ {"Koto ", 107, MIDI_UNMAPPED}, // +++ (PQ2)
+ {"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}, // ? (LB1)
+ {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, // R ? (Iceman)
+ {"PiccoloKA ", 72, MIDI_UNMAPPED}, // +++ (KQ1)
{"PinkBassMS", 39, MIDI_UNMAPPED},
- {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (LB1) */
- {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
- {"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) */
+ {"Pizz2 ", 45, MIDI_UNMAPPED}, // ++ (LB1)
+ {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (KQ1)
+ {"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}, /* + (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 (QFG1) */
- {"Some Birds", 123, MIDI_UNMAPPED}, /* + (LB1) */
- {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (Iceman) */
- {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (LB1) */
- {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (Camelot) */
+ {"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 (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}, /* ++ (QFG1) */
- {"String MS", 45, MIDI_UNMAPPED}, /* + (Camelot) */
+ {"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) */
+ {"Syn Brass4", 63, MIDI_UNMAPPED}, // ++ (PQ2)
{"SynBass MS", 38, MIDI_UNMAPPED},
- {"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}, /* +++ (LB1) */
- {"Tom MS", 117, 48}, /* +++ (Iceman) */
- {"Toms MS", 117, 48}, /* +++ (Camelot, QFG1) */
- {"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */
- {"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}, /* + (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) */
+ {"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}, // +++ (LB1)
+ {"Tom MS", 117, 48}, // +++ (Iceman)
+ {"Toms MS", 117, 48}, // +++ (Camelot, QFG1)
+ {"Tpt1prtl ", 56, MIDI_UNMAPPED}, // +++ (KQ1)
+ {"TriangleMS", 112, 81}, // R (Camelot)
+ {"Trumpet 1 ", 56, MIDI_UNMAPPED}, // +++ (Camelot)
+ {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, // + (Iceman)
+ {"Warm Pad" , 89, MIDI_UNMAPPED}, // ++ (PQ3)
+ {"WaterBells", 98, MIDI_UNMAPPED}, // + (PQ2)
+ {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, // ? (KQ1)
+ {"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}
};
+ typedef Common::List<Mt32ToGmMap> Mt32ToGmMapList;
+ extern Mt32ToGmMapList *Mt32dynamicMappings;
+
} // End of namespace Sci
+
+#endif // SCI_SOUND_DRIVERS_MAP_MT32_TO_GM_H
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index dc229262c7..6611753420 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -32,11 +32,14 @@
#include "sound/softsynth/emumidi.h"
#include "sci/resource.h"
+#include "sci/sound/drivers/gm_names.h"
#include "sci/sound/drivers/mididriver.h"
#include "sci/sound/drivers/map-mt32-to-gm.h"
namespace Sci {
+Mt32ToGmMapList *Mt32dynamicMappings = NULL;
+
class MidiPlayer_Midi : public MidiPlayer {
public:
enum {
@@ -53,9 +56,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);
@@ -130,10 +134,21 @@ MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _pla
_sysExBuf[1] = 0x10;
_sysExBuf[2] = 0x16;
_sysExBuf[3] = 0x12;
+
+ Mt32dynamicMappings = new Mt32ToGmMapList();
}
MidiPlayer_Midi::~MidiPlayer_Midi() {
delete _driver;
+
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ delete[] (*it).name;
+ (*it).name = 0;
+ }
+
+ Mt32dynamicMappings->clear();
+ delete Mt32dynamicMappings;
}
void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
@@ -328,12 +343,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;
@@ -607,22 +629,40 @@ void MidiPlayer_Midi::readMt32DrvData() {
byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) {
int i = 0;
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ if (scumm_strnicmp(iname, (*it).name, 10) == 0)
+ return getGmInstrument((*it));
+ }
+ }
+
while (Mt32MemoryTimbreMaps[i].name) {
if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
return getGmInstrument(Mt32MemoryTimbreMaps[i]);
i++;
}
+
return MIDI_UNMAPPED;
}
byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) {
int i = 0;
+ if (Mt32dynamicMappings != NULL) {
+ const Mt32ToGmMapList::iterator end = Mt32dynamicMappings->end();
+ for (Mt32ToGmMapList::iterator it = Mt32dynamicMappings->begin(); it != end; ++it) {
+ if (scumm_strnicmp(iname, (*it).name, 10) == 0)
+ return (*it).gmRhythmKey;
+ }
+ }
+
while (Mt32MemoryTimbreMaps[i].name) {
if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0)
return Mt32MemoryTimbreMaps[i].gmRhythmKey;
i++;
}
+
return MIDI_UNMAPPED;
}
@@ -902,7 +942,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:
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/music.cpp b/engines/sci/sound/music.cpp
index e2540e1a8f..252ea5489c 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -66,7 +66,17 @@ 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));
+
+ 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) {
@@ -83,6 +93,9 @@ 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("native_fb01"))
_pMidiDrv = MidiPlayer_Fb01_create(_soundVersion);
@@ -102,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) {
@@ -144,8 +158,10 @@ void SciMusic::sendMidiCommandsFromQueue() {
}
void SciMusic::clearPlayList() {
- Common::StackLock lock(_mutex);
-
+ // we must NOT lock our mutex here. Playlist is modified inside soundKill() which will lock the mutex
+ // during deletion. If we lock it here, a deadlock may occur within soundStop() because that one
+ // calls the mixer, which will also lock the mixer mutex and if the mixer thread is active during
+ // that time, we will get a deadlock.
while (!_playList.empty()) {
soundStop(_playList[0]);
soundKill(_playList[0]);
@@ -291,6 +307,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;
@@ -383,8 +401,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();
@@ -431,6 +455,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);
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index b10f64aa18..9fcbb9346d 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -148,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);
@@ -222,6 +223,7 @@ private:
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 84aedc8515..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);
@@ -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) {
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index a06939dc51..ca1dc8869f 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -691,6 +691,7 @@ void Actor_v3::walkActor() {
int Actor::remapDirection(int dir, bool is_walking) {
int specdir;
byte flags;
+ byte mask;
bool flipX;
bool flipY;
@@ -769,6 +770,14 @@ int Actor::remapDirection(int dir, bool is_walking) {
case 6:
return 180;
}
+
+ // MM C64 stores flags as a part of the mask
+ if (_vm->_game.version == 0) {
+ mask = _vm->getMaskFromBox(_walkbox);
+ // face the wall if climbing/descending a ladder
+ if ((mask & 0x8C) == 0x84)
+ return 0;
+ }
}
// OR 1024 in to signal direction interpolation should be done
return normalizeAngle(dir) | 1024;
@@ -1043,9 +1052,17 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY)
// yDist must be divided by 4, as we are using 8x2 pixels
// blocks for actor coordinates).
int xDist = ABS(x - destX);
- int yDist = ABS(y - destY) / 4;
+ int yDist;
int dist;
+ // MM C64: This fixes the trunk bug (#3070065), as well
+ // as the fruit bowl, however im not sure if its
+ // the proper solution or not.
+ if( g_scumm->_game.version == 0 )
+ yDist = ABS(y - destY);
+ else
+ yDist = ABS(y - destY) / 4;
+
if (xDist < yDist)
dist = (xDist >> 1) + yDist;
else
@@ -1073,6 +1090,7 @@ AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) {
abr.x = foundX;
abr.y = foundY;
abr.box = box;
+
break;
}
if (dist < bestDist) {
@@ -2159,7 +2177,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();
}
}
@@ -2254,7 +2277,7 @@ void Actor::setActorCostume(int c) {
}
}
-static const char* v0ActorNames[0x19] = {
+static const char* v0ActorNames_English[25] = {
"Syd",
"Razor",
"Dave",
@@ -2270,10 +2293,36 @@ static const char* v0ActorNames[0x19] = {
"Purple Tentacle",
"Green Tentacle",
"Meteor",
+ "",
+ "",
+ "",
"Plant",
"",
"",
"",
+ "Sandy"
+};
+
+static const char* v0ActorNames_German[25] = {
+ "Syd",
+ "Razor",
+ "Dave",
+ "Michael",
+ "Bernard",
+ "Wendy",
+ "Jeff",
+ "",
+ "Dr.Fred",
+ "Schwester Edna",
+ "Weird Ed",
+ "Ted",
+ "Lila Tentakel",
+ "Gr<nes Tentakel",
+ "Meteor",
+ "",
+ "",
+ "",
+ "Pflanze",
"",
"",
"",
@@ -2284,8 +2333,15 @@ const byte *Actor::getActorName() {
const byte *ptr = NULL;
if (_vm->_game.version == 0) {
- if (_number)
- ptr = (const byte *)v0ActorNames[_number - 1];
+ if (_number) {
+ switch (_vm->_language) {
+ case Common::DE_DEU:
+ ptr = (const byte *)v0ActorNames_German[_number - 1];
+ break;
+ default:
+ ptr = (const byte *)v0ActorNames_English[_number - 1];
+ }
+ }
} else {
ptr = _vm->getResourceAddress(rtActorName, _number);
}
@@ -2556,6 +2612,21 @@ void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {
#endif
+void ActorC64::saveLoadWithSerializer(Serializer *ser) {
+ Actor::saveLoadWithSerializer(ser);
+
+ static const SaveLoadEntry actorEntries[] = {
+ MKLINE(ActorC64, _costCommand, sleByte, VER(84)),
+ MKLINE(ActorC64, _costFrame, sleByte, VER(84)),
+ MKLINE(ActorC64, _miscflags, sleByte, VER(84)),
+ MKLINE(ActorC64, _speaking, sleByte, VER(84)),
+ MKLINE(ActorC64, _speakingPrev, sleByte, VER(84)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, actorEntries);
+}
+
void Actor::saveLoadWithSerializer(Serializer *ser) {
static const SaveLoadEntry actorEntries[] = {
MKLINE(Actor, _pos.x, sleInt16, VER(8)),
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
index 88ba9902b4..98854ec5ba 100644
--- a/engines/scumm/actor.h
+++ b/engines/scumm/actor.h
@@ -305,7 +305,7 @@ public:
void classChanged(int cls, bool value);
// Used by the save/load system:
- void saveLoadWithSerializer(Serializer *ser);
+ virtual void saveLoadWithSerializer(Serializer *ser);
protected:
bool isInClass(int cls);
@@ -381,14 +381,16 @@ protected:
class ActorC64 : public Actor_v2 {
public:
- // FIXME: These vars are never saved, which might lead to broken save states.
- byte _miscflags;
- byte _speaking, _speakingPrev;
byte _costCommand, _costFrame;
+ byte _miscflags; // 0x1: strong, 0x8: Ed's enemy, 0x40: stop moving, 0x80: hide(dead/radiation suit)
+ byte _speaking, _speakingPrev;
public:
ActorC64(ScummEngine *scumm, int id) : Actor_v2(scumm, id) {
- _speaking = _speakingPrev = _costCommand = _costFrame = 0;
+ _costCommand = 0;
+ _costFrame = 0;
+ _speaking = 0;
+ _speakingPrev = 0;
}
virtual void initActor(int mode) {
Actor_v2::initActor(mode);
@@ -397,6 +399,9 @@ public:
}
}
+ // Used by the save/load system:
+ virtual void saveLoadWithSerializer(Serializer *ser);
+
protected:
};
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index d5d6b6182b..354a1d4491 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -1087,10 +1087,10 @@ void AkosRenderer::akos16SetupBitReader(const byte *src) {
}
#define AKOS16_FILL_BITS() \
- if (_akos16.numbits <= 8) { \
- _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \
- _akos16.numbits += 8; \
- }
+ if (_akos16.numbits <= 8) { \
+ _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \
+ _akos16.numbits += 8; \
+ }
#define AKOS16_EAT_BITS(n) \
_akos16.numbits -= (n); \
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
index dc6f10696f..fb8e128415 100644
--- a/engines/scumm/boxes.cpp
+++ b/engines/scumm/boxes.cpp
@@ -614,10 +614,8 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) {
box->lr.x = bp->c64.x2;
box->lr.y = bp->c64.y2;
- if (bp->c64.mask & 0x88) {
+ if ((bp->c64.mask & 0x88) == 0x88) {
// walkbox for (right/left) corner
- // TODO: ladders (incl. man-eating plant) have mask 0x8A,
- // must those walkboxes be adjusted?
if (bp->c64.mask & 0x04)
box->ur = box->ul;
else
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index fa4804ce7d..e75a45212e 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -52,20 +52,15 @@ void ScummEngine::loadCJKFont() {
_newLineCharacter = 0;
if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji
- int numChar = 256 * 32;
- _2byteWidth = 16;
- _2byteHeight = 16;
- // use FM-TOWNS font rom, since game files don't have kanji font resources
- if (!fp.open("fmt_fnt.rom")) {
- error("SCUMM::Font: Couldn't open fmt_fnt.rom");
- } else {
- _useCJKMode = true;
- debug(2, "Loading FM-TOWNS Kanji rom");
- _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
- fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
- fp.close();
- }
+#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE
+ error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build");
+#endif
+ // use FM-TOWNS font rom, since game files don't have kanji font resources
+ _cjkFont = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns);
+ if (!_cjkFont)
+ error("SCUMM::Font: Couldn't open file 'FMT_FNT.ROM'");
_textSurfaceMultiplier = 2;
+ _useCJKMode = true;
} else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) {
int numChar = 3418;
_2byteWidth = 12;
@@ -167,90 +162,6 @@ void ScummEngine::loadCJKFont() {
}
}
-static int SJIStoFMTChunk(int f, int s) { //converts sjis code to fmt font offset
- enum {
- KANA = 0,
- KANJI = 1,
- EKANJI = 2
- };
- int base = s - ((s + 1) % 32);
- int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA;
-
- if (f >= 0x81 && f <= 0x84) kanjiType = KANA;
- if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI;
- if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI;
-
- if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) {
- c = 48; //correction
- p = -8; //correction
- }
-
- if (kanjiType == KANA) {//Kana
- chunk_f = (f - 0x81) * 2;
- } else if (kanjiType == KANJI) {//Standard Kanji
- p += f - 0x88;
- chunk_f = c + 2 * p;
- } else if (kanjiType == EKANJI) {//Enhanced Kanji
- p += f - 0xe0;
- chunk_f = c + 2 * p;
- }
-
- // Base corrections
- if (base == 0x7f && s == 0x7f)
- base -= 0x20;
- if (base == 0x9f && s == 0xbe)
- base += 0x20;
- if (base == 0xbf && s == 0xde)
- base += 0x20;
- //if (base == 0x7f && s == 0x9e)
- // base += 0x20;
-
- switch (base) {
- case 0x3f:
- cr = 0; //3f
- if (kanjiType == KANA) chunk = 1;
- else if (kanjiType == KANJI) chunk = 31;
- else if (kanjiType == EKANJI) chunk = 111;
- break;
- case 0x5f:
- cr = 0; //5f
- if (kanjiType == KANA) chunk = 17;
- else if (kanjiType == KANJI) chunk = 47;
- else if (kanjiType == EKANJI) chunk = 127;
- break;
- case 0x7f:
- cr = -1; //80
- if (kanjiType == KANA) chunk = 9;
- else if (kanjiType == KANJI) chunk = 63;
- else if (kanjiType == EKANJI) chunk = 143;
- break;
- case 0x9f:
- cr = 1; //9e
- if (kanjiType == KANA) chunk = 2;
- else if (kanjiType == KANJI) chunk = 32;
- else if (kanjiType == EKANJI) chunk = 112;
- break;
- case 0xbf:
- cr = 1; //be
- if (kanjiType == KANA) chunk = 18;
- else if (kanjiType == KANJI) chunk = 48;
- else if (kanjiType == EKANJI) chunk = 128;
- break;
- case 0xdf:
- cr = 1; //de
- if (kanjiType == KANA) chunk = 10;
- else if (kanjiType == KANJI) chunk = 64;
- else if (kanjiType == EKANJI) chunk = 144;
- break;
- default:
- debug(4, "Invalid Char! f %x s %x base %x c %d p %d", f, s, base, c, p);
- return 0;
- }
-
- debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr);
- return ((chunk_f + chunk) * 32 + (s - base)) + cr;
-}
-
static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offset
// rangeTbl maps SJIS char-codes to the PCE System Card font rom.
// Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range.
@@ -327,9 +238,8 @@ byte *ScummEngine::get2byteCharPtr(int idx) {
}
idx = (SWAP_CONSTANT_16(idx) & 0x7fff) - 1;
- } else {
- idx = SJIStoFMTChunk((idx % 256), (idx / 256));
}
+
break;
case Common::ZH_TWN:
{
@@ -453,30 +363,79 @@ 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, 8, 9, 8, 9, 8, 9, 0, 0, 0 };
+ static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
+ static const uint8 sjisFontHeightI4[] = { 0, 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 (_vm->_game.version == 3) ? 8 : 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->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if ((chr & 0xff00) == 0xfd00) {
+ chr &= 0xff;
+ } else if (chr >= 256) {
+ spacing = 8;
+ } else if (useTownsFontRomCharacter(chr)) {
+ spacing = 4;
+ }
+
+ if (spacing) {
+ if (_vm->_game.id == GID_MONKEY) {
+ spacing++;
+ if (_curId == 2)
+ spacing++;
+ } else if (_vm->_game.id != GID_INDY4 && _curId == 1) {
+ spacing++;
+ }
+ }
+
+ } else if (chr >= 0x80) {
+ 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;
}
+bool CharsetRendererClassic::useTownsFontRomCharacter(uint16 chr) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_vm->_game.platform != Common::kPlatformFMTowns || !_vm->_useCJKMode)
+ return false;
+
+ if (chr < 128) {
+ if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127))
+ return true;
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
int CharsetRenderer::getStringWidth(int arg, const byte *text) {
int pos = 0;
int width = 1;
- byte chr;
+ int chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -534,12 +493,20 @@ 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 (checkSJISCode(chr))
+ // This strange character conversion is the exact way the original does it here.
+ // This is the only way to get an accurate text formatting in the MI1 intro.
+ chr = (int8)text[pos++] | (chr << 8);
+ } else if (chr & 0x80) {
+ pos++;
+ width += _vm->_2byteWidth;
+ continue;
+ }
}
+ width += getCharWidth(chr);
}
setCurID(oldID);
@@ -550,7 +517,7 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
int lastspace = -1;
int curw = 1;
- byte chr;
+ int chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -612,9 +579,17 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
if (chr == _vm->_newLineCharacter)
lastspace = pos - 1;
- if ((chr & 0x80) && _vm->_useCJKMode) {
- pos++;
- curw += _vm->_2byteWidth;
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if (checkSJISCode(chr))
+ // This strange character conversion is the exact way the original does it here.
+ // This is the only way to get an accurate text formatting in the MI1 intro.
+ chr = (int8)str[pos++] | (chr << 8);
+ curw += getCharWidth(chr);
+ } else if (chr & 0x80) {
+ pos++;
+ curw += _vm->_2byteWidth;
+ }
} else {
curw += getCharWidth(chr);
}
@@ -631,12 +606,22 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
setCurID(oldID);
}
-int CharsetRendererV3::getCharWidth(byte chr) {
- if (chr & 0x80 && _vm->_useCJKMode)
- return _vm->_2byteWidth / 2;
+int CharsetRendererV3::getCharWidth(uint16 chr) {
int spacing = 0;
- spacing = *(_widthTable + chr);
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if (chr >= 256)
+ spacing = 8;
+ else if (chr >= 128)
+ spacing = 4;
+ } else if (chr & 0x80) {
+ spacing = _vm->_2byteWidth / 2;
+ }
+ }
+
+ if (!spacing)
+ spacing = *(_widthTable + chr);
return spacing;
}
@@ -655,6 +640,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();
@@ -673,17 +666,37 @@ void CharsetRendererCommon::enableShadow(bool enable) {
if (enable) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
_shadowColor = 8;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
+ if (_vm->_cjkFont) {
+ if (_vm->_game.version == 5) {
+ if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) ||
+ ((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) ||
+ ((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode);
+ } else {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ }
+ _vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3);
+ } else {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode);
+ }
+ }
+#endif
_shadowMode = kFMTOWNSShadowMode;
} else {
_shadowColor = 0;
_shadowMode = kNormalShadowMode;
}
} else {
+ if (_vm->_cjkFont) {
+ _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ _vm->_cjkFont->toggleFlippedMode(false);
+ }
_shadowMode = kNoShadowMode;
}
}
-
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
@@ -696,7 +709,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
VirtScreen *vs;
const byte *charPtr;
byte *dst;
- int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+ int is2byte = (chr >= 256 && _vm->_useCJKMode) ? 1 : 0;
assertRange(0, _curId, _vm->_numCharsets - 1, "charset");
@@ -706,10 +719,16 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
if (chr == '@')
return;
- if (is2byte) {
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_useCJKMode && chr > 127) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ charPtr = 0;
+ width = _vm->_cjkFont->getCharWidth(chr);
+ height = _vm->_cjkFont->getFontHeight();
+ } else {
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ charPtr = _vm->get2byteCharPtr(chr);
+ }
} else {
charPtr = _fontPtr + chr * 8;
width = getCharWidth(chr);
@@ -744,18 +763,31 @@ 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) &&
+#endif
+ (ignoreCharsetMask || !vs->hasTwoBuffers)) {
dst = vs->getPixels(_left, drawTop);
- drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
+ if (charPtr)
+ drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
+ else if (_vm->_cjkFont)
+ _vm->_cjkFont->drawChar(dst, chr, vs->pitch, vs->bytesPerPixel, _color, _shadowColor);
} else {
dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier);
- drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel);
+ if (charPtr)
+ drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel, (_vm->_textSurfaceMultiplier == 2 && !is2byte));
+ else if (_vm->_cjkFont)
+ _vm->_cjkFont->drawChar(dst, chr, _vm->_textSurface.pitch, vs->bytesPerPixel, _color, _shadowColor);
+ if (is2byte)
+ origWidth /= _vm->_textSurfaceMultiplier;
}
if (_str.left > _left)
_str.left = _left;
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ _left += origWidth;
if (_str.right < _left) {
_str.right = _left;
@@ -773,9 +805,17 @@ void CharsetRendererV3::drawChar(int chr, const Graphics::Surface &s, int x, int
int width, height;
int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
if (is2byte) {
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ width = _vm->_cjkFont->getCharWidth(chr);
+ height = _vm->_cjkFont->getFontHeight();
+ dst = (byte *)s.pixels + y * s.pitch + x;
+ _vm->_cjkFont->drawChar(dst, chr, s.pitch, s.bytesPerPixel, _color, _shadowColor);
+ return;
+ } else {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ }
} else {
charPtr = _fontPtr + chr * 8;
// width = height = 8;
@@ -801,6 +841,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)),
@@ -822,7 +885,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
int offsX, offsY;
VirtScreen *vs;
const byte *charPtr;
- bool is2byte = (chr >= 0x80 && _vm->_useCJKMode);
+ bool is2byte = (chr >= 256 && _vm->_useCJKMode);
assertRange(1, _curId, _vm->_numCharsets - 1, "charset");
@@ -836,12 +899,44 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_vm->_charsetColorMap[1] = _color;
- if (is2byte) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ processTownsCharsetColors(_bytesPerPixel);
+ bool noSjis = false;
+
+ if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) {
+ if ((chr & 0x00ff) == 0x00fd) {
+ chr >>= 8;
+ noSjis = true;
+ }
+ }
+
+ if (useTownsFontRomCharacter(chr) && !noSjis) {
+ charPtr = 0;
+ _vm->_cjkChar = chr;
enableShadow(true);
+
+ width = getCharWidth(chr);
+ // For whatever reason MI1 uses a different font width
+ // for alignment calculation and for drawing when
+ // charset 2 is active. This fixes some subtle glitches.
+ if (_vm->_game.id == GID_MONKEY && _curId == 2)
+ width--;
+ origWidth = width;
+
+ origHeight = height = getFontHeight();
+ offsX = offsY = 0;
+ } else
+#endif
+ if (_vm->_useCJKMode && (chr >= 128) && !noSjis) {
+ enableShadow(true);
+ origWidth = width = _vm->_2byteWidth;
+ origHeight = height = _vm->_2byteHeight;
charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
offsX = offsY = 0;
+ if (_shadowMode != kNoShadowMode) {
+ width++;
+ height++;
+ }
} else {
uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
assert(charOffs < 0x14000);
@@ -849,8 +944,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
return;
charPtr = _fontPtr + charOffs;
- width = charPtr[0];
- height = charPtr[1];
+ width = origWidth = charPtr[0];
+ height = origHeight = charPtr[1];
if (_disableOffsX) {
offsX = 0;
@@ -862,13 +957,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
charPtr += 4; // Skip over char header
}
- origWidth = width;
- origHeight = height;
- if (_shadowMode != kNoShadowMode) {
- width++;
- height++;
- }
if (_firstChar) {
_str.left = 0;
_str.top = 0;
@@ -879,8 +968,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_top += offsY;
_left += offsX;
- if (_left + origWidth / _vm->_textSurfaceMultiplier > _right + 1 || _left < 0) {
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ if (_left + origWidth > _right + 1 || _left < 0) {
+ _left += origWidth;
_top -= offsY;
return;
}
@@ -905,23 +994,29 @@ 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;
}
printCharIntern(is2byte, charPtr, origWidth, origHeight, width, height, vs, ignoreCharsetMask);
- _left += origWidth / _vm->_textSurfaceMultiplier;
+ _left += origWidth;
if (_str.right < _left) {
_str.right = _left;
- if (_shadowMode != kNoShadowMode)
+ if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode != kNoShadowMode)
_str.right++;
}
- if (_str.bottom < _top + height / _vm->_textSurfaceMultiplier)
- _str.bottom = _top + height / _vm->_textSurfaceMultiplier;
+ if (_str.bottom < _top + origHeight)
+ _str.bottom = _top + origHeight;
_top -= offsY;
}
@@ -961,7 +1056,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 {
@@ -980,10 +1079,12 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
drawTop = _top - _vm->_screenTop;
}
- if (is2byte) {
+ if (!charPtr && _vm->_cjkFont)
+ _vm->_cjkFont->drawChar(dstPtr, _vm->_cjkChar, dstSurface.pitch, dstSurface.bytesPerPixel, _vm->_townsCharsetColorMap[1], _shadowColor);
+ else if (is2byte) {
drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight, dstSurface.bytesPerPixel);
} else {
- drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight);
+ drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight, _vm->_textSurfaceMultiplier == 2);
}
if (_blitAlso && vs->hasTwoBuffers) {
@@ -1031,9 +1132,17 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x
if (is2byte) {
enableShadow(true);
- charPtr = _vm->get2byteCharPtr(chr);
- width = _vm->_2byteWidth;
- height = _vm->_2byteHeight;
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ width = _vm->_cjkFont->getCharWidth(chr);
+ height = _vm->_cjkFont->getFontHeight();
+ dst = (byte *)s.pixels + y * s.pitch + x;
+ _vm->_cjkFont->drawChar(dst, chr, s.pitch, s.bytesPerPixel, _color, _shadowColor);
+ return;
+ } else {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ }
} else {
uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
assert(charOffs < 0x10000);
@@ -1056,23 +1165,55 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x
}
}
-void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) {
+void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height,
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ bool scale2x) {
+#else
+ bool) {
+#endif
+
int y, x;
int color;
byte numbits, bits;
+ byte *dst2 = dst;
+ int pitch = s.pitch - width;
+
assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
bits = *src++;
numbits = 8;
+ byte *cmap = _vm->_charsetColorMap;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_vm->_game.platform == Common::kPlatformFMTowns)
+ cmap = _vm->_townsCharsetColorMap;
+ if (scale2x) {
+ dst2 += s.pitch;
+ pitch <<= 1;
+ }
+#endif
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];
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x)
+ dst[1] = dst2[0] = dst2[1] = dst[0];
+#endif
}
dst++;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst++;
+ dst2 += 2;
+ }
+#endif
+
bits <<= bpp;
numbits -= bpp;
if (numbits == 0) {
@@ -1080,13 +1221,37 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
numbits = 8;
}
}
- dst += s.pitch - width;
+ dst += pitch;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ dst2 += pitch;
+#endif
}
}
-void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth,
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ bool scale2x) {
+#else
+ bool) {
+#endif
+
int y, x;
byte bits = 0;
+ uint8 col = _color;
+ int pitch = s.pitch - width * bitDepth;
+ byte *dst2 = dst + s.pitch;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ byte *dst3 = dst2;
+ byte *dst4 = dst2;
+ if (scale2x) {
+ dst3 = dst2 + s.pitch;
+ dst4 = dst3 + s.pitch;
+ pitch <<= 1;
+ }
+ if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5)
+ col = _vm->_townsCharsetColorMap[1];
+#endif
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
@@ -1103,23 +1268,49 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con
WRITE_UINT16(dst, _vm->_16BitPalette[_color]);
} else {
if (_shadowMode != kNoShadowMode) {
- *(dst + 1) = _shadowColor;
- *(dst + s.pitch) = _shadowColor;
- if (_shadowMode != kFMTOWNSShadowMode)
- *(dst + s.pitch + 1) = _shadowColor;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor;
+ dst3[0] = dst4[0] = dst3[1] = dst4[1] = _shadowColor;
+ } else
+#endif
+ {
+ dst[1] = dst2[0] = _shadowColor;
+ if (_shadowMode != kFMTOWNSShadowMode)
+ dst2[1] = _shadowColor;
+ }
}
- *dst = _color;
+ dst[0] = col;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x)
+ dst[1] = dst2[0] = dst2[1] = col;
+#endif
}
}
dst += bitDepth;
+ dst2 += bitDepth;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (scale2x) {
+ dst++;
+ dst2++;
+ dst3 += 2;
+ dst4 += 2;
+ }
+#endif
}
- dst += s.pitch - width * bitDepth;
+ dst += pitch;
+ dst2 += pitch;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ dst3 += pitch;
+ dst4 += pitch;
+#endif
}
}
#ifdef USE_RGB_COLOR
-void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
int y, x;
int bitCount = 0;
byte bits = 0;
@@ -1191,7 +1382,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);
}
@@ -1349,7 +1540,7 @@ void CharsetRendererNES::drawChar(int chr, const Graphics::Surface &s, int x, in
drawBits1(s, dst, charPtr, y, width, height, s.bytesPerPixel);
}
-void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
+void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) {
for (int i = 0; i < 8; i++) {
byte c0 = src[i];
byte c1 = src[i + 8];
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index dca254669b..991ea2c8ad 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -27,6 +27,7 @@
#include "common/scummsys.h"
#include "common/rect.h"
+#include "graphics/sjis.h"
#include "scumm/gfx.h"
#include "scumm/saveload.h"
@@ -37,9 +38,9 @@ class NutRenderer;
struct VirtScreen;
static inline bool checkSJISCode(byte c) {
- if ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea /* && c <= 0xff */))
- return false;
- return true;
+ if ((c >= 0x80 && c <= 0x9f) || (c >= 0xe0 && c <= 0xfd))
+ return true;
+ return false;
}
@@ -80,12 +81,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(); }
@@ -108,7 +113,8 @@ protected:
ShadowMode _shadowMode;
void enableShadow(bool enable);
- virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
+
public:
CharsetRendererCommon(ScummEngine *vm);
@@ -120,7 +126,7 @@ public:
class CharsetRendererClassic : public CharsetRendererCommon {
protected:
- void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
+ void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, bool scale2x = false);
void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask);
@@ -130,14 +136,19 @@ 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);
+
+ // Some SCUMM 5 games contain hard coded logic to determine whether to use
+ // the SCUMM fonts or the FM-Towns font rom to draw a character. For the other
+ // games we will simply check for a character greater 127.
+ bool useTownsFontRomCharacter(uint16 chr);
};
class CharsetRendererNES : public CharsetRendererCommon {
protected:
byte *_trTable;
- void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
public:
CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {}
@@ -147,7 +158,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,13 +172,13 @@ 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
class CharsetRendererPCE : public CharsetRendererV3 {
protected:
- void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth);
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false);
public:
CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {}
@@ -185,7 +196,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 +215,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/debugger.cpp b/engines/scumm/debugger.cpp
index b5a4070f0b..a348d9c942 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -35,7 +35,6 @@
#include "scumm/debugger.h"
#include "scumm/imuse/imuse.h"
#include "scumm/object.h"
-#include "scumm/player_v2.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
@@ -87,7 +86,7 @@ ScummDebugger::ScummDebugger(ScummEngine *s)
if (_vm->_game.id == GID_LOOM)
DCmd_Register("drafts", WRAP_METHOD(ScummDebugger, Cmd_PrintDraft));
- if (_vm->_game.id == GID_MONKEY && Common::kPlatformSegaCD)
+ if (_vm->_game.id == GID_MONKEY && _vm->_game.platform == Common::kPlatformSegaCD)
DCmd_Register("passcode", WRAP_METHOD(ScummDebugger, Cmd_Passcode));
DCmd_Register("loadgame", WRAP_METHOD(ScummDebugger, Cmd_LoadGame));
@@ -529,7 +528,7 @@ bool ScummDebugger::Cmd_Debug(int argc, const char **argv) {
} else {
DebugPrintf("Usage: debug [+CHANNEL|-CHANNEL]\n");
DebugPrintf("Enables or disables the given debug channel.\n");
- DebugPrintf("When used without parameters, lists all avaiable debug channels and their status.\n");
+ DebugPrintf("When used without parameters, lists all available debug channels and their status.\n");
}
return true;
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 9010cb84c3..467282bd43 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1198,12 +1198,7 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int
int minutes = infos.time & 0xFF;
desc.setSaveTime(hour, minutes);
-
- minutes = infos.playtime / 60;
- hour = minutes / 60;
- minutes %= 60;
-
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(infos.playtime * 1000);
}
return desc;
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 98fab9468a..a5542ca868 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -220,6 +220,7 @@ static const GameSettings gameVariantsTable[] = {
{"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 | 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},
@@ -243,10 +244,12 @@ static const GameSettings gameVariantsTable[] = {
{"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},
@@ -367,7 +370,7 @@ static const GameSettings gameVariantsTable[] = {
#ifdef USE_RGB_COLOR
// Added 16bit color
{"arttime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
- {"baseball2001", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
+ {"baseball2001", 0, 0, GID_BASEBALL2001, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"readtime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"SoccerMLS", 0, 0, GID_SOCCER, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
{"spyozon", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 1e0bf6d4be..093a103cc8 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -45,7 +45,6 @@
#include "scumm/scumm.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/player_v2.h"
#include "scumm/verbs.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
@@ -473,14 +472,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/file_nes.cpp b/engines/scumm/file_nes.cpp
index 0caf63b410..cd761db81a 100644
--- a/engines/scumm/file_nes.cpp
+++ b/engines/scumm/file_nes.cpp
@@ -1077,7 +1077,7 @@ uint16 ScummNESFile::extractResource(Common::WriteStream *output, const Resource
case NES_PREPLIST:
len = res->length;
- reslen += write_word(output, 0x002A);
+ reslen += write_word(output, 0x002A);
reslen += write_byte(output, ' ');
for (i = 1; i < 8; i++)
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 3b8d9c296a..8daee06a88 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;
@@ -1027,30 +1013,51 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
if (rect.left > vs->w)
return;
-
+
// Convert 'rect' to local (virtual screen) coordinates
rect.top -= vs->topline;
rect.bottom -= vs->topline;
rect.clip(vs->w, vs->h);
+ const int height = rect.height();
+ const int width = rect.width();
+
+#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
+
markRectAsDirty(vs->number, rect, USAGE_BIT_RESTORED);
screenBuf = vs->getPixels(rect.left, rect.top);
- const int height = rect.height();
- const int width = rect.width();
-
if (!height)
return;
if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) {
blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height, 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;
@@ -2403,7 +2457,7 @@ void ScummEngine::decodeNESBaseTiles() {
}
static const int v1MMNEScostTables[2][6] = {
- /* desc lens offs data gfx pal */
+ /* desc lens offs data gfx pal */
{ 25, 27, 29, 31, 33, 35},
{ 26, 28, 30, 32, 34, 36}
};
@@ -3653,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);
@@ -3679,6 +3737,10 @@ void ScummEngine::fadeOut(int effect) {
// Just blit screen 0 to the display (i.e. display will be black)
vs->setDirtyRange(0, vs->h);
updateDirtyScreen(kMainVirtScreen);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->update();
+#endif
break;
case 134:
dissolveEffect(1, 1);
@@ -3856,15 +3918,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) {
@@ -3904,23 +3963,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;
@@ -3929,21 +3986,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;
@@ -3952,21 +4009,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;
@@ -3975,21 +4033,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/logic_he.cpp b/engines/scumm/he/logic_he.cpp
index add9b982e2..36aeaf4a3c 100644
--- a/engines/scumm/he/logic_he.cpp
+++ b/engines/scumm/he/logic_he.cpp
@@ -232,7 +232,7 @@ int32 LogicHErace::op_1101(int32 *args) {
int32 retval;
float temp;
- temp = args[0] / _userData[532];
+ temp = args[0] / _userData[532];
if (_userData[519] != temp) {
_userData[519] = temp;
op_sub3(temp);
@@ -955,6 +955,30 @@ int LogicHEsoccer::op_1021(int32 *args) {
}
/***********************
+ * Backyard Baseball 2001
+ *
+ */
+
+int LogicHEbaseball2001::versionID() {
+ return 1;
+}
+
+int32 LogicHEbaseball2001::dispatch(int op, int numArgs, int32 *args) {
+ int res = 0;
+
+ switch (op) {
+ case 3001:
+ // Check network status
+ break;
+
+ default:
+ LogicHE::dispatch(op, numArgs, args);
+ }
+
+ return res;
+}
+
+/***********************
* Backyard Basketball
*
*/
diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h
index 7dd141c5b1..ab952abd5e 100644
--- a/engines/scumm/he/logic_he.h
+++ b/engines/scumm/he/logic_he.h
@@ -133,6 +133,14 @@ private:
int op_1021(int32 *args);
};
+class LogicHEbaseball2001 : public LogicHE {
+public:
+ LogicHEbaseball2001(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+};
+
class LogicHEbasketball : public LogicHE {
public:
LogicHEbasketball(ScummEngine_v90he *vm) : LogicHE(vm) {}
diff --git a/engines/scumm/he/palette_he.cpp b/engines/scumm/he/palette_he.cpp
index 6ef68d981e..ad3f90b8eb 100644
--- a/engines/scumm/he/palette_he.cpp
+++ b/engines/scumm/he/palette_he.cpp
@@ -203,8 +203,8 @@ void ScummEngine_v90he::setHEPaletteFromImage(int palSlot, int resId, int state)
uint8 *data = getResourceAddress(rtImage, resId);
assert(data);
const uint8 *rgbs = findWrappedBlock(MKID_BE('RGBS'), data, state, 0);
- assert(rgbs);
- setHEPaletteFromPtr(palSlot, rgbs);
+ if (rgbs)
+ setHEPaletteFromPtr(palSlot, rgbs);
}
void ScummEngine_v90he::setHEPaletteFromRoom(int palSlot, int resId, int state) {
diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp
index c259c3ffd2..b42441ceaf 100644
--- a/engines/scumm/he/resource_he.cpp
+++ b/engines/scumm/he/resource_he.cpp
@@ -23,7 +23,6 @@
*
*/
-
#include "scumm/scumm.h"
#include "scumm/file.h"
#include "scumm/he/intern_he.h"
@@ -40,9 +39,13 @@
namespace Scumm {
+#if defined(SCUMM_LITTLE_ENDIAN)
+#define LE16(x)
+#define LE32(x)
+#elif defined(SCUMM_BIG_ENDIAN)
#define LE16(x) ((x) = TO_LE_16(x))
#define LE32(x) ((x) = TO_LE_32(x))
-
+#endif
ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
: _vm(scumm) {
@@ -207,9 +210,9 @@ int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte
}
-/* res_type_id_to_string:
- * Translate a numeric resource type to it's corresponding string type.
- * (For informative-ness.)
+/**
+ * Translate a numeric resource type to it's corresponding string type.
+ * (For informative-ness.)
*/
const char *Win32ResExtractor::res_type_id_to_string(int id) {
if (id == 241)
@@ -219,9 +222,9 @@ const char *Win32ResExtractor::res_type_id_to_string(int id) {
return NULL;
}
-/* res_type_string_to_id:
- * Translate a resource type string to integer.
- * (Used to convert the --type option.)
+/**
+ * Translate a resource type string to integer.
+ * (Used to convert the --type option.)
*/
const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
static const char *res_type_ids[] = {
@@ -242,22 +245,18 @@ const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
return type;
}
-/* return the resource id quoted if it's a string, otherwise just return it */
-char *Win32ResExtractor::WinResource::get_resource_id_quoted() {
- // FIXME: Using a static var here is EVIL and in fact, broken when
- // used multiple times in a row, e.g. in a single call to printf()
- // or debug()... which is in fact how we use this function... :-)
- static char tmp[WINRES_ID_MAXLEN+2];
-
+/**
+ * Return the resource id quoted if it is a string, otherwise (i.e. if
+ * it is numeric) just return it.
+ */
+Common::String Win32ResExtractor::WinResource::getQuotedResourceId() const {
if (numeric_id || id[0] == '\0')
return id;
-
- sprintf(tmp, "'%s'", id);
- return tmp;
+ return '"' + Common::String(id) + '"';
}
int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
- WinResource *type_wr, WinResource *name_wr,
+ WinResource *type_wr, WinResource *name_wr,
WinResource *lang_wr, byte **data) {
int size;
bool free_it;
@@ -281,19 +280,21 @@ int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
if ((id = strtol(type_wr->id, 0, 10)) != 0)
type = res_type_id_to_string(id);
- debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]",
- name_wr->get_resource_id_quoted(),
- (lang_wr->id[0] != '\0' ? " language: " : ""),
- lang_wr->get_resource_id_quoted(), size);
-
+ if (lang_wr != NULL && lang_wr->id[0] != '\0') {
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s language: %s [size=%d]",
+ name_wr->getQuotedResourceId().c_str(), lang_wr->getQuotedResourceId().c_str(), size);
+ } else {
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s [size=%d]",
+ name_wr->getQuotedResourceId().c_str(), size);
+ }
return size;
}
-/* extract_resource:
- * Extract a resource, returning pointer to data.
+/**
+ * Extract a resource, returning pointer to data.
*/
byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size,
- bool *free_it, char *type, char *lang, bool raw) {
+ bool *free_it, char *type, char *lang, bool raw) {
char *str;
int32 intval;
@@ -320,20 +321,20 @@ byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *
return NULL;
}
-/* extract_group_icon_resource:
- * Create a complete RT_GROUP_ICON resource, that can be written to
- * an `.ico' file without modifications. Returns an allocated
- * memory block that should be freed with free() once used.
+/**
+ * Create a complete RT_GROUP_ICON resource, that can be written to
+ * an `.ico' file without modifications. Returns an allocated
+ * memory block that should be freed with free() once used.
*
- * `root' is the offset in file that specifies the resource.
- * `base' is the offset that string pointers are calculated from.
- * `ressize' should point to an integer variable where the size of
- * the returned memory block will be placed.
- * `is_icon' indicates whether resource to be extracted is icon
- * or cursor group.
+ * `root' is the offset in file that specifies the resource.
+ * `base' is the offset that string pointers are calculated from.
+ * `ressize' should point to an integer variable where the size of
+ * the returned memory block will be placed.
+ * `is_icon' indicates whether resource to be extracted is icon
+ * or cursor group.
*/
byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang,
- int *ressize, bool is_icon) {
+ int *ressize, bool is_icon) {
Win32CursorIconDir *icondir;
Win32CursorIconFileDir *fileicondir;
byte *memory;
@@ -374,20 +375,20 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
}
if (get_resource_entry(fi, fwr, &iconsize) != NULL) {
- if (iconsize == 0) {
+ if (iconsize == 0) {
debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", _fileName.c_str(), name);
skipped++;
continue;
- }
- if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) {
+ }
+ if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) {
debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)",
_fileName.c_str(), name, iconsize, FROM_LE_32(icondir->entries[c].bytes_in_res));
- }
- size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */
+ }
+ size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */
- /* cursor resources have two additional WORDs that contain
- * hotspot info */
- if (!is_icon)
+ /* cursor resources have two additional WORDs that contain
+ * hotspot info */
+ if (!is_icon)
size -= sizeof(uint16)*2;
}
}
@@ -428,8 +429,8 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
return NULL;
}
if (size == 0) {
- skipped++;
- continue;
+ skipped++;
+ continue;
}
/* copy ICONDIRENTRY (not including last dwImageOffset) */
@@ -465,10 +466,10 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR
return memory;
}
-/* check_offset:
- * Check if a chunk of data (determined by offset and size)
- * is within the bounds of the WinLibrary file.
- * Usually not called directly.
+/**
+ * Check if a chunk of data (determined by offset and size)
+ * is within the bounds of the WinLibrary file.
+ * Usually not called directly.
*/
bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) {
int need_size = (int)((byte *)offset - memory + size);
@@ -485,8 +486,8 @@ bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *n
}
-/* do_resources:
- * Do something for each resource matching type, name and lang.
+/**
+ * Do something for each resource matching type, name and lang.
*/
int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, byte **data) {
WinResource *type_wr;
@@ -494,7 +495,7 @@ int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name
WinResource *lang_wr;
int size;
- type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1);
+ type_wr = (WinResource *)calloc(3, sizeof(WinResource));
name_wr = type_wr + 1;
lang_wr = type_wr + 2;
@@ -521,14 +522,11 @@ int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base,
/* get a list of all resources at this level */
wr = list_resources(fi, base, &rescnt);
if (wr == NULL) {
- if (size != 0)
- return size;
- else
- return 0;
+ return size;
}
/* process each resource listed */
- for (c = 0 ; c < rescnt ; c++) {
+ for (c = 0; c < rescnt; c++) {
/* (over)write the corresponding WinResource holder with the current */
memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource));
@@ -555,7 +553,9 @@ bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) {
return false;
if (id[0] == '-')
id++;
- if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2)
+ cmp1 = strtol(wr->id, 0, 10);
+ cmp2 = strtol(id, 0, 10);
+ if (!cmp1 || !cmp2 || cmp1 != cmp2)
return false;
} else {
if (id[0] == '-')
@@ -643,9 +643,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary
}
-/* list_resources:
- * Return an array of WinResource's in the current
- * resource level specified by _res->
+/**
+ * Return an array of WinResource's in the current
+ * resource level specified by _res->
*/
Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) {
if (res != NULL && !res->is_directory)
@@ -657,10 +657,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi
count);
}
-/* read_library:
- * Read header and get resource directory offset in a Windows library
- * (AKA module).
- *
+/**
+ * Read header and get resource directory offset in a Windows library
+ * (AKA module).
*/
bool Win32ResExtractor::read_library(WinLibrary *fi) {
/* check for DOS header signature `MZ' */
@@ -695,7 +694,9 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) {
/* calc_vma_size has reported error */
return false;
}
- fi->memory = (byte *)realloc(fi->memory, fi->total_size);
+ byte *ptr = (byte *)realloc(fi->memory, fi->total_size);
+ assert(ptr);
+ fi->memory = ptr;
/* relocate memory, start from last section */
pe_header = PE_HEADER(fi->memory);
@@ -739,13 +740,13 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) {
return false;
}
-/* calc_vma_size:
- * Calculate the total amount of memory needed for a 32-bit Windows
- * module. Returns -1 if file was too small.
+/**
+ * Calculate the total amount of memory needed for a 32-bit Windows
+ * module. Returns -1 if file was too small.
*/
int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
- Win32ImageSectionHeader *seg;
- int c, segcount, size;
+ Win32ImageSectionHeader *seg;
+ int c, segcount, size;
size = 0;
RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections);
@@ -760,7 +761,7 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
seg = PE_SECTIONS(fi->memory);
RETURN_IF_BAD_POINTER(-1, *seg);
- for (c = 0 ; c < segcount ; c++) {
+ for (c = 0 ; c < segcount ; c++) {
RETURN_IF_BAD_POINTER(0, *seg);
fix_win32_image_section_header(seg);
@@ -768,9 +769,9 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
/* I have no idea what misc.virtual_size is for... */
size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size);
seg++;
- }
+ }
- return size;
+ return size;
}
Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) {
@@ -914,8 +915,8 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int
if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)",
- entries[c].dib_size,
- (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
+ entries[c].dib_size,
+ (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
);
image_data = (byte *)malloc(image_size);
@@ -977,9 +978,9 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int
row[4*x+2] = (color >> 0) & 0xFF;
}
if (bitmap.bit_count == 32)
- row[4*x+3] = (color >> 24) & 0xFF;
+ row[4*x+3] = (color >> 24) & 0xFF;
else
- row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
+ row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
*/
}
@@ -1051,93 +1052,93 @@ uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) {
}
void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) {
- LE16(obj->reserved);
+ LE16(obj->reserved);
LE16(obj->type);
- LE16(obj->count);
+ LE16(obj->count);
}
void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) {
- LE32(obj->size);
- LE32(obj->width);
- LE32(obj->height);
- LE16(obj->planes);
- LE16(obj->bit_count);
- LE32(obj->compression);
- LE32(obj->size_image);
- LE32(obj->x_pels_per_meter);
- LE32(obj->y_pels_per_meter);
- LE32(obj->clr_used);
- LE32(obj->clr_important);
+ LE32(obj->size);
+ LE32(obj->width);
+ LE32(obj->height);
+ LE16(obj->planes);
+ LE16(obj->bit_count);
+ LE32(obj->compression);
+ LE32(obj->size_image);
+ LE32(obj->x_pels_per_meter);
+ LE32(obj->y_pels_per_meter);
+ LE32(obj->clr_used);
+ LE32(obj->clr_important);
}
void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) {
- LE16(obj->hotspot_x);
- LE16(obj->hotspot_y);
- LE32(obj->dib_size);
- LE32(obj->dib_offset);
+ LE16(obj->hotspot_x);
+ LE16(obj->hotspot_y);
+ LE32(obj->dib_size);
+ LE32(obj->dib_offset);
}
void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) {
- LE32(obj->misc.physical_address);
- LE32(obj->virtual_address);
- LE32(obj->size_of_raw_data);
- LE32(obj->pointer_to_raw_data);
- LE32(obj->pointer_to_relocations);
- LE32(obj->pointer_to_linenumbers);
- LE16(obj->number_of_relocations);
- LE16(obj->number_of_linenumbers);
- LE32(obj->characteristics);
+ LE32(obj->misc.physical_address);
+ LE32(obj->virtual_address);
+ LE32(obj->size_of_raw_data);
+ LE32(obj->pointer_to_raw_data);
+ LE32(obj->pointer_to_relocations);
+ LE32(obj->pointer_to_linenumbers);
+ LE16(obj->number_of_relocations);
+ LE16(obj->number_of_linenumbers);
+ LE32(obj->characteristics);
}
/* fix_win32_image_header_endian:
* NOTE: This assumes that the optional header is always available.
*/
void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) {
- LE32(obj->signature);
- LE16(obj->file_header.machine);
- LE16(obj->file_header.number_of_sections);
- LE32(obj->file_header.time_date_stamp);
- LE32(obj->file_header.pointer_to_symbol_table);
- LE32(obj->file_header.number_of_symbols);
- LE16(obj->file_header.size_of_optional_header);
- LE16(obj->file_header.characteristics);
+ LE32(obj->signature);
+ LE16(obj->file_header.machine);
+ LE16(obj->file_header.number_of_sections);
+ LE32(obj->file_header.time_date_stamp);
+ LE32(obj->file_header.pointer_to_symbol_table);
+ LE32(obj->file_header.number_of_symbols);
+ LE16(obj->file_header.size_of_optional_header);
+ LE16(obj->file_header.characteristics);
// FIXME: Does this assert ever trigger? If so, we should modify this function
// to properly deal with it.
assert(obj->file_header.size_of_optional_header >= sizeof(obj->optional_header));
- LE16(obj->optional_header.magic);
- LE32(obj->optional_header.size_of_code);
- LE32(obj->optional_header.size_of_initialized_data);
- LE32(obj->optional_header.size_of_uninitialized_data);
- LE32(obj->optional_header.address_of_entry_point);
- LE32(obj->optional_header.base_of_code);
- LE32(obj->optional_header.base_of_data);
- LE32(obj->optional_header.image_base);
- LE32(obj->optional_header.section_alignment);
- LE32(obj->optional_header.file_alignment);
- LE16(obj->optional_header.major_operating_system_version);
- LE16(obj->optional_header.minor_operating_system_version);
- LE16(obj->optional_header.major_image_version);
- LE16(obj->optional_header.minor_image_version);
- LE16(obj->optional_header.major_subsystem_version);
- LE16(obj->optional_header.minor_subsystem_version);
- LE32(obj->optional_header.win32_version_value);
- LE32(obj->optional_header.size_of_image);
- LE32(obj->optional_header.size_of_headers);
- LE32(obj->optional_header.checksum);
- LE16(obj->optional_header.subsystem);
- LE16(obj->optional_header.dll_characteristics);
- LE32(obj->optional_header.size_of_stack_reserve);
- LE32(obj->optional_header.size_of_stack_commit);
- LE32(obj->optional_header.size_of_heap_reserve);
- LE32(obj->optional_header.size_of_heap_commit);
- LE32(obj->optional_header.loader_flags);
- LE32(obj->optional_header.number_of_rva_and_sizes);
+ LE16(obj->optional_header.magic);
+ LE32(obj->optional_header.size_of_code);
+ LE32(obj->optional_header.size_of_initialized_data);
+ LE32(obj->optional_header.size_of_uninitialized_data);
+ LE32(obj->optional_header.address_of_entry_point);
+ LE32(obj->optional_header.base_of_code);
+ LE32(obj->optional_header.base_of_data);
+ LE32(obj->optional_header.image_base);
+ LE32(obj->optional_header.section_alignment);
+ LE32(obj->optional_header.file_alignment);
+ LE16(obj->optional_header.major_operating_system_version);
+ LE16(obj->optional_header.minor_operating_system_version);
+ LE16(obj->optional_header.major_image_version);
+ LE16(obj->optional_header.minor_image_version);
+ LE16(obj->optional_header.major_subsystem_version);
+ LE16(obj->optional_header.minor_subsystem_version);
+ LE32(obj->optional_header.win32_version_value);
+ LE32(obj->optional_header.size_of_image);
+ LE32(obj->optional_header.size_of_headers);
+ LE32(obj->optional_header.checksum);
+ LE16(obj->optional_header.subsystem);
+ LE16(obj->optional_header.dll_characteristics);
+ LE32(obj->optional_header.size_of_stack_reserve);
+ LE32(obj->optional_header.size_of_stack_commit);
+ LE32(obj->optional_header.size_of_heap_reserve);
+ LE32(obj->optional_header.size_of_heap_commit);
+ LE32(obj->optional_header.loader_flags);
+ LE32(obj->optional_header.number_of_rva_and_sizes);
}
void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) {
- LE32(obj->virtual_address);
- LE32(obj->size);
+ LE32(obj->virtual_address);
+ LE32(obj->size);
}
diff --git a/engines/scumm/he/resource_he.h b/engines/scumm/he/resource_he.h
index 4567c598a3..65190ea41c 100644
--- a/engines/scumm/he/resource_he.h
+++ b/engines/scumm/he/resource_he.h
@@ -184,7 +184,7 @@ class Win32ResExtractor : public ResExtractor {
bool numeric_id;
bool is_directory;
- char *get_resource_id_quoted();
+ Common::String getQuotedResourceId() const;
} PACKED_STRUCT;
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 3555f55d95..ca4a65ac74 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -34,7 +34,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/he/sprite_he.h"
@@ -1623,13 +1622,11 @@ void ScummEngine_v100he::o100_roomOps() {
case 137:
byte buffer[256];
- int r;
copyScriptString((byte *)buffer, sizeof(buffer));
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp
index 8ade78c1b5..9d62a31f6d 100644
--- a/engines/scumm/he/script_v60he.cpp
+++ b/engines/scumm/he/script_v60he.cpp
@@ -283,15 +283,14 @@ void ScummEngine_v60he::o60_roomOps() {
break;
case 221:
byte buffer[100];
- int len, r;
+ int len;
convertMessageToString(_scriptPointer, buffer, sizeof(buffer));
len = resStrLen(_scriptPointer);
_scriptPointer += len + 1;
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index 979b2b3df4..9b160151b0 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -31,7 +31,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/verbs.h"
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index baa57c7821..76daacbd54 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -36,7 +36,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/util.h"
@@ -173,7 +172,7 @@ int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
}
const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
- (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+ (idx2 - FROM_LE_32(ah->dim2start)) + (idx1 - FROM_LE_32(ah->dim1start));
switch (FROM_LE_32(ah->type)) {
case kByteArray:
@@ -711,13 +710,11 @@ void ScummEngine_v72he::o72_roomOps() {
case 221:
byte buffer[256];
- int r;
copyScriptString((byte *)buffer, sizeof(buffer));
- r = convertFilePath(buffer, sizeof(buffer));
- memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r);
- debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName);
+ _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer));
+ debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index dd44180fa0..b06dc712d9 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -35,7 +35,6 @@
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp
index 6acc16a804..841eba960d 100644
--- a/engines/scumm/he/script_v90he.cpp
+++ b/engines/scumm/he/script_v90he.cpp
@@ -32,7 +32,6 @@
#include "scumm/he/logic_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
-#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/he/sprite_he.h"
@@ -1948,42 +1947,51 @@ void ScummEngine_v90he::getArrayDim(int array, int *dim2start, int *dim2end, int
}
}
+static int sortArrayOffset;
+
static int compareByteArray(const void *a, const void *b) {
- int va = *((const uint8 *)a);
- int vb = *((const uint8 *)a);
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)b + sortArrayOffset);
return va - vb;
}
static int compareByteArrayReverse(const void *a, const void *b) {
- int va = *((const uint8 *)a);
- int vb = *((const uint8 *)a);
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)b + sortArrayOffset);
return vb - va;
}
static int compareIntArray(const void *a, const void *b) {
- int va = (int16)READ_LE_UINT16((const uint8 *)a);
- int vb = (int16)READ_LE_UINT16((const uint8 *)b);
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return va - vb;
}
static int compareIntArrayReverse(const void *a, const void *b) {
- int va = (int16)READ_LE_UINT16((const uint8 *)a);
- int vb = (int16)READ_LE_UINT16((const uint8 *)b);
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
return vb - va;
}
static int compareDwordArray(const void *a, const void *b) {
- int va = (int32)READ_LE_UINT32((const uint8 *)a);
- int vb = (int32)READ_LE_UINT32((const uint8 *)b);
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return va - vb;
}
static int compareDwordArrayReverse(const void *a, const void *b) {
- int va = (int32)READ_LE_UINT32((const uint8 *)a);
- int vb = (int32)READ_LE_UINT32((const uint8 *)b);
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
return vb - va;
}
+
+/**
+ * Sort a row range in a two-dimensional array by the value in a given column.
+ *
+ * We sort the data in the row range [dim2start..dim2end], according to the value
+ * in column dim1start == dim1end.
+ */
void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder) {
debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, dim2start, dim2end, dim1start, dim1end, sortOrder);
@@ -1992,11 +2000,21 @@ void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
assert(ah);
- const int num = dim2end - dim2start + 1;
- const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
- const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start))
- + dim1start - FROM_LE_32(ah->dim1start);
-
+ const int num = dim2end - dim2start + 1; // number of rows to sort
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1; // length of a row = number of columns in it
+ const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start)); // memory offset to the first row to be sorted
+ sortArrayOffset = dim1start - FROM_LE_32(ah->dim1start); // offset to the column by which we sort
+
+ // Now we just have to invoke qsort on the appropriate row range. We
+ // need to pass sortArrayOffset as an implicit parameter to the
+ // comparison functions, which makes it necessary to use a global
+ // (albeit local to this file) variable.
+ // This could be avoided by using qsort_r or a self-written portable
+ // analog (this function passes an additional, user determined
+ // parameter to the comparison function).
+ // Another idea would be to use Common::sort, but that only is
+ // suitable if you sort objects of fixed size, which must be known
+ // during compilation time; clearly this not the case here.
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
@@ -2039,7 +2057,6 @@ void ScummEngine_v90he::o90_sortArray() {
int dim2end = pop();
int dim2start = pop();
getArrayDim(array, &dim2start, &dim2end, &dim1start, &dim1end);
- checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
sortArray(array, dim2start, dim2end, dim1start, dim1end, sortOrder);
}
break;
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 7bfd1de4cf..318cb818b6 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -241,7 +241,7 @@ int SoundHE::isSoundCodeUsed(int sound) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
return _heChannel[chan].sbngBlock;
} else {
return 0;
@@ -255,7 +255,7 @@ int SoundHE::getSoundPos(int sound) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
int time = _vm->getHETimer(chan + 4) * _heChannel[chan].rate / 1000;
return time;
} else {
@@ -276,7 +276,7 @@ int SoundHE::getSoundVar(int sound, int var) {
chan = i;
}
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) {
+ if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
return _heChannel[chan].soundVars[var];
} else {
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index 1ac7e98689..ddbbb2101f 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -358,6 +358,7 @@ static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w,
void Wiz::writeColor(uint8 *dstPtr, int dstType, uint16 color) {
switch (dstType) {
+ case kDstCursor:
case kDstScreen:
WRITE_UINT16(dstPtr, color);
break;
@@ -1519,7 +1520,7 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int
cw = width;
ch = height;
dstPitch = cw * _vm->_bytesPerPixel;
- dstType = kDstMemory;
+ dstType = (_cursorImage) ? kDstCursor : kDstMemory;
} else {
if (dstResNum) {
uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
@@ -2088,7 +2089,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/he/wiz_he.h b/engines/scumm/he/wiz_he.h
index 1fa9564486..c255e27d14 100644
--- a/engines/scumm/he/wiz_he.h
+++ b/engines/scumm/he/wiz_he.h
@@ -145,7 +145,8 @@ enum {
enum DstSurface {
kDstScreen = 0,
kDstMemory = 1,
- kDstResource = 2
+ kDstResource = 2,
+ kDstCursor = 3
};
class ScummEngine_v71he;
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 6b38f80df1..a90915e438 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -192,13 +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
-
- //////////// REMOVE
- //_parser = MidiParser_createEUP();
- ///////////
-
} else if (!memcmp(ptr, "FORM", 4)) {
// Humongous Games XMIDI resource
_parser = MidiParser::createParser_XMIDI();
diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
index 3876bd4e80..f2e50382b3 100644
--- a/engines/scumm/insane/insane.cpp
+++ b/engines/scumm/insane/insane.cpp
@@ -944,7 +944,7 @@ bool Insane::actor1StateFlags(int state) {
bool retvalue = 0;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] <= state)
break;
@@ -1099,7 +1099,7 @@ bool Insane::actor0StateFlags1(int state) {
bool retvalue = 1;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] >= state)
break;
@@ -1119,7 +1119,7 @@ bool Insane::actor0StateFlags2(int state) {
bool retvalue = 1;
unsigned int i;
- for (i = 0; i < sizeof(spans); i++) {
+ for (i = 0; i < ARRAYSIZE(spans); i++) {
retvalue = !retvalue;
if (spans[i] >= state)
break;
diff --git a/engines/scumm/insane/insane_ben.cpp b/engines/scumm/insane/insane_ben.cpp
index 9ddb4c6670..05775f1585 100644
--- a/engines/scumm/insane/insane_ben.cpp
+++ b/engines/scumm/insane/insane_ben.cpp
@@ -1163,7 +1163,7 @@ void Insane::actor02Reaction(int32 buttons) {
setBenState();
_actor[0].act[2].tilt = 0;
// for some reason there is no break at this
- // place, so tilt gets overriden on next line
+ // place, so tilt gets overridden on next line
}
_actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
break;
diff --git a/engines/scumm/insane/insane_enemy.cpp b/engines/scumm/insane/insane_enemy.cpp
index 2291b2c37b..e8d97d3875 100644
--- a/engines/scumm/insane/insane_enemy.cpp
+++ b/engines/scumm/insane/insane_enemy.cpp
@@ -1328,7 +1328,7 @@ void Insane::turnEnemy(bool battle) {
if (_actor[1].damage < _actor[1].maxdamage) {
_actor[1].lost = false;
} else {
- if (!_actor[1].lost && !_actor[1].lost) {
+ if (!_actor[1].lost && !_actor[0].lost) {
_actor[1].lost = true;
_actor[1].act[2].state = 36;
_actor[1].act[1].state = 36;
@@ -2072,7 +2072,7 @@ void Insane::actor12Reaction(int32 buttons) {
setEnemyState();
_actor[1].act[2].tilt = 0;
// for some reason there is no break at this
- // place, so tilt gets overriden on next line
+ // place, so tilt gets overridden on next line
}
_actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
break;
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 14d1f5fdd4..1a60564a9e 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 \
@@ -40,6 +41,7 @@ MODULE_OBJS := \
player_v1.o \
player_v2.o \
player_v2a.o \
+ player_v2base.o \
player_v2cms.o \
player_v3a.o \
player_v4a.o \
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index c6ac53b862..c44043ca81 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -315,6 +315,10 @@ int ScummEngine::getObjectIndex(int object) const {
return -1;
for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_game.version == 0 )
+ if( _objs[i].flags != _v0ObjectFlag )
+ continue;
+
if (_objs[i].obj_nr == object)
return i;
}
@@ -526,6 +530,9 @@ int ScummEngine::findObject(int x, int y) {
#endif
if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
_objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) {
+ // MMC64: Set the object search flag
+ if (_game.version == 0)
+ _v0ObjectFlag = _objs[i].flags;
if (_game.version == 0 && _v0ObjectIndex)
return i;
else
@@ -714,7 +721,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() {
@@ -993,6 +1000,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->flags = Gdi::dbAllowMaskOr;
if (_game.version == 8) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
od->parent = cdhd->v7.parent;
@@ -1008,6 +1016,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0;
} else if (_game.version == 7) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
od->parent = cdhd->v7.parent;
@@ -1020,6 +1029,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte *
od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir);
} else if (_game.version == 6) {
+ assert(imhd);
od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
od->width = READ_LE_UINT16(&cdhd->v6.w);
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 7659e5fe1a..672af1e191 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 {
@@ -70,7 +71,7 @@ void ScummEngine::resetPalette() {
// 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.
- 0xFF, 0x55, 0xFF
+ 0x7F, 0x3B, 0xA6
};
static const byte tableNESPalette[] = {
@@ -139,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?
@@ -198,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);
}
@@ -465,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);
@@ -506,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);
@@ -973,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);
}
@@ -1069,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_nes.h b/engines/scumm/player_nes.h
index b2eafb79b0..89292f7c24 100644
--- a/engines/scumm/player_nes.h
+++ b/engines/scumm/player_nes.h
@@ -75,8 +75,6 @@ private:
void APU_writeControl(byte value);
byte APU_readStatus();
- void do_mix(int16 *buf, uint len);
-
ScummEngine *_vm;
Audio::Mixer *_mixer;
Audio::SoundHandle _soundHandle;
diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp
index a17aab4d59..4236fb2d6b 100644
--- a/engines/scumm/player_pce.cpp
+++ b/engines/scumm/player_pce.cpp
@@ -152,7 +152,7 @@ static const uint16 sounds[13][6] = {
// 0xB2A1
static const byte data_table[482] = {
/* 0*/ 0xE2, 0x0A, 0xE1, 0x0D, 0xE6, 0xED, 0xE0, 0x0F, 0xE2, 0x00, 0xE1, 0x00,
- 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2,
0xB2, 0xE1, 0x03, 0xF2, 0xF2, 0xB2, 0xE1, 0x04, 0xF2, 0xF2, 0xB2, 0xE1,
0x05, 0xF2, 0xF2, 0xB2, 0xE1, 0x06, 0xF2, 0xF2, 0xB2, 0xE1, 0x07, 0xF2,
0xF2, 0xB2, 0xE1, 0x08, 0xF2, 0xF2, 0xB2, 0xE1, 0x09, 0xF2, 0xF2, 0xB2,
@@ -208,10 +208,10 @@ static const byte data_table[482] = {
/*395*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x0B, 0xE8, 0x0B, 0xFF,
/*408*/ 0xE2, 0x0C, 0xE1, 0x03, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
- 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00,
+ 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF,
/*467*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x1B, 0xE8, 0x1B, 0xFF,
/*480*/ 0xFF,
@@ -238,11 +238,11 @@ private:
double _clock;
double _rate;
uint8 _select;
- uint8 _balance;
- channel_t _channel[8];
- int16 _volumeTable[32];
- uint32 _noiseFreqTable[32];
- uint32 _waveFreqTable[4096];
+ uint8 _balance;
+ channel_t _channel[8];
+ int16 _volumeTable[32];
+ uint32 _noiseFreqTable[32];
+ uint32 _waveFreqTable[4096];
public:
void init();
@@ -255,180 +255,180 @@ public:
PSG_HuC6280::PSG_HuC6280(double clock, double samplerate) {
_clock = clock;
- _rate = samplerate;
+ _rate = samplerate;
- // Initialize PSG_HuC6280 emulator
- init();
+ // Initialize PSG_HuC6280 emulator
+ init();
}
void PSG_HuC6280::init() {
- int i;
- double step;
+ int i;
+ double step;
- // Loudest volume level for table
- double level = 65535.0 / 6.0 / 32.0;
+ // Loudest volume level for table
+ double level = 65535.0 / 6.0 / 32.0;
- // Clear context
+ // Clear context
reset();
- // Make waveform frequency table
- for(i = 0; i < 4096; i++) {
- step = ((_clock / _rate) * 4096) / (i+1);
- _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;
- }
-
- // Make noise frequency table
- for(i = 0; i < 32; i++) {
- step = ((_clock / _rate) * 32) / (i+1);
- _noiseFreqTable[i] = (uint32)step;
- }
-
- // Make volume table
- // PSG_HuC6280 has 48dB volume range spread over 32 steps
- step = 48.0 / 32.0;
- for(i = 0; i < 31; i++) {
- _volumeTable[i] = (uint16)level;
- level /= pow(10.0, step / 20.0);
- }
- _volumeTable[31] = 0;
+ // Make waveform frequency table
+ for(i = 0; i < 4096; i++) {
+ step = ((_clock / _rate) * 4096) / (i+1);
+ _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step;
+ }
+
+ // Make noise frequency table
+ for(i = 0; i < 32; i++) {
+ step = ((_clock / _rate) * 32) / (i+1);
+ _noiseFreqTable[i] = (uint32)step;
+ }
+
+ // Make volume table
+ // PSG_HuC6280 has 48dB volume range spread over 32 steps
+ step = 48.0 / 32.0;
+ for(i = 0; i < 31; i++) {
+ _volumeTable[i] = (uint16)level;
+ level /= pow(10.0, step / 20.0);
+ }
+ _volumeTable[31] = 0;
}
void PSG_HuC6280::reset() {
_select = 0;
- _balance = 0xFF;
- memset(_channel, 0, sizeof(_channel));
- memset(_volumeTable, 0, sizeof(_volumeTable));
- memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable));
- memset(_waveFreqTable, 0, sizeof(_waveFreqTable));
+ _balance = 0xFF;
+ memset(_channel, 0, sizeof(_channel));
+ memset(_volumeTable, 0, sizeof(_volumeTable));
+ memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable));
+ memset(_waveFreqTable, 0, sizeof(_waveFreqTable));
}
void PSG_HuC6280::write(int offset, byte data) {
- channel_t *chan = &_channel[_select];
-
- switch(offset & 0x0F) {
- case 0x00: // Channel select
- _select = data & 0x07;
- break;
-
- case 0x01: // Global balance
- _balance = data;
- break;
-
- case 0x02: // Channel frequency (LSB)
- chan->frequency = (chan->frequency & 0x0F00) | data;
- chan->frequency &= 0x0FFF;
- break;
-
- case 0x03: // Channel frequency (MSB)
- chan->frequency = (chan->frequency & 0x00FF) | (data << 8);
- chan->frequency &= 0x0FFF;
- break;
-
- case 0x04: // Channel control (key-on, DDA mode, volume)
- // 1-to-0 transition of DDA bit resets waveform index
- if((chan->control & 0x40) && ((data & 0x40) == 0)) {
- chan->index = 0;
- }
- chan->control = data;
- break;
-
- case 0x05: // Channel balance
- chan->balance = data;
- break;
-
- case 0x06: // Channel waveform data
- switch(chan->control & 0xC0) {
- case 0x00:
- chan->waveform[chan->index & 0x1F] = data & 0x1F;
- chan->index = (chan->index + 1) & 0x1F;
- break;
-
- case 0x40:
- break;
-
- case 0x80:
- chan->waveform[chan->index & 0x1F] = data & 0x1F;
- chan->index = (chan->index + 1) & 0x1F;
- break;
-
- case 0xC0:
- chan->dda = data & 0x1F;
- break;
- }
-
- break;
-
- case 0x07: // Noise control (enable, frequency)
- case 0x08: // LFO frequency
- case 0x09: // LFO control (enable, mode)
- break;
-
- default:
- break;
- }
+ channel_t *chan = &_channel[_select];
+
+ switch(offset & 0x0F) {
+ case 0x00: // Channel select
+ _select = data & 0x07;
+ break;
+
+ case 0x01: // Global balance
+ _balance = data;
+ break;
+
+ case 0x02: // Channel frequency (LSB)
+ chan->frequency = (chan->frequency & 0x0F00) | data;
+ chan->frequency &= 0x0FFF;
+ break;
+
+ case 0x03: // Channel frequency (MSB)
+ chan->frequency = (chan->frequency & 0x00FF) | (data << 8);
+ chan->frequency &= 0x0FFF;
+ break;
+
+ case 0x04: // Channel control (key-on, DDA mode, volume)
+ // 1-to-0 transition of DDA bit resets waveform index
+ if((chan->control & 0x40) && ((data & 0x40) == 0)) {
+ chan->index = 0;
+ }
+ chan->control = data;
+ break;
+
+ case 0x05: // Channel balance
+ chan->balance = data;
+ break;
+
+ case 0x06: // Channel waveform data
+ switch(chan->control & 0xC0) {
+ case 0x00:
+ chan->waveform[chan->index & 0x1F] = data & 0x1F;
+ chan->index = (chan->index + 1) & 0x1F;
+ break;
+
+ case 0x40:
+ break;
+
+ case 0x80:
+ chan->waveform[chan->index & 0x1F] = data & 0x1F;
+ chan->index = (chan->index + 1) & 0x1F;
+ break;
+
+ case 0xC0:
+ chan->dda = data & 0x1F;
+ break;
+ }
+
+ break;
+
+ case 0x07: // Noise control (enable, frequency)
+ case 0x08: // LFO frequency
+ case 0x09: // LFO control (enable, mode)
+ break;
+
+ default:
+ break;
+ }
}
void PSG_HuC6280::update(int16* samples, int sampleCnt) {
- static const int scale_tab[] = {
- 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
- 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
- };
- int ch;
- int i;
+ static const int scale_tab[] = {
+ 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
+ 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
+ };
+ int ch;
+ int i;
- int lmal = (_balance >> 4) & 0x0F;
- int rmal = (_balance >> 0) & 0x0F;
- int vll, vlr;
+ int lmal = (_balance >> 4) & 0x0F;
+ int rmal = (_balance >> 0) & 0x0F;
+ int vll, vlr;
- lmal = scale_tab[lmal];
- rmal = scale_tab[rmal];
+ lmal = scale_tab[lmal];
+ rmal = scale_tab[rmal];
- // Clear buffer
+ // Clear buffer
memset(samples, 0, 2 * sampleCnt * sizeof(int16));
- for(ch = 0; ch < 6; ch++) {
- // Only look at enabled channels
- if(_channel[ch].control & 0x80) {
- int lal = (_channel[ch].balance >> 4) & 0x0F;
- int ral = (_channel[ch].balance >> 0) & 0x0F;
- int al = _channel[ch].control & 0x1F;
-
- lal = scale_tab[lal];
- ral = scale_tab[ral];
-
- // Calculate volume just as the patent says
- vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
- if(vll > 0x1F) vll = 0x1F;
-
- vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
- if(vlr > 0x1F) vlr = 0x1F;
-
- vll = _volumeTable[vll];
- vlr = _volumeTable[vlr];
-
- // Check channel mode
- if(_channel[ch].control & 0x40) {
- /* DDA mode */
- for(i = 0; i < sampleCnt; i++) {
- samples[2*i] += (int16)(vll * (_channel[ch].dda - 16));
- samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));
- }
- } else {
- /* Waveform mode */
- uint32 step = _waveFreqTable[_channel[ch].frequency];
- for(i = 0; i < sampleCnt; i += 1) {
- int offset;
- int16 data;
- offset = (_channel[ch].counter >> 12) & 0x1F;
- _channel[ch].counter += step;
- _channel[ch].counter &= 0x1FFFF;
- data = _channel[ch].waveform[offset];
- samples[2*i] += (int16)(vll * (data - 16));
- samples[2*i + 1] += (int16)(vlr * (data - 16));
- }
- }
- }
- }
+ for(ch = 0; ch < 6; ch++) {
+ // Only look at enabled channels
+ if(_channel[ch].control & 0x80) {
+ int lal = (_channel[ch].balance >> 4) & 0x0F;
+ int ral = (_channel[ch].balance >> 0) & 0x0F;
+ int al = _channel[ch].control & 0x1F;
+
+ lal = scale_tab[lal];
+ ral = scale_tab[ral];
+
+ // Calculate volume just as the patent says
+ vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
+ if(vll > 0x1F) vll = 0x1F;
+
+ vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
+ if(vlr > 0x1F) vlr = 0x1F;
+
+ vll = _volumeTable[vll];
+ vlr = _volumeTable[vlr];
+
+ // Check channel mode
+ if(_channel[ch].control & 0x40) {
+ /* DDA mode */
+ for(i = 0; i < sampleCnt; i++) {
+ samples[2*i] += (int16)(vll * (_channel[ch].dda - 16));
+ samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16));
+ }
+ } else {
+ /* Waveform mode */
+ uint32 step = _waveFreqTable[_channel[ch].frequency];
+ for(i = 0; i < sampleCnt; i += 1) {
+ int offset;
+ int16 data;
+ offset = (_channel[ch].counter >> 12) & 0x1F;
+ _channel[ch].counter += step;
+ _channel[ch].counter &= 0x1FFFF;
+ data = _channel[ch].waveform[offset];
+ samples[2*i] += (int16)(vll * (data - 16));
+ samples[2*i + 1] += (int16)(vlr * (data - 16));
+ }
+ }
+ }
+ }
}
@@ -466,7 +466,7 @@ void Player_PCE::procA541(channel_t *channel) {
channel->controlVec24 = false;
channel->controlVec21 = 0;
- channel->waveformCtrl = 0x80;
+ channel->waveformCtrl = 0x80;
}
// A592
diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp
index 871bd67546..06f97fd671 100644
--- a/engines/scumm/player_towns.cpp
+++ b/engines/scumm/player_towns.cpp
@@ -29,33 +29,184 @@
namespace Scumm {
-Player_Towns::Player_Towns(ScummEngine *vm, Audio::Mixer *mixer) : _vm(vm) {
+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;
- memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound));
_cdaVolLeft = _cdaVolRight = 0;
_eupVolLeft = _eupVolRight = 0;
- memset(&_ovrCur, 0, sizeof(SoundOvrParameters));
- _soundOverride = 0;
+ _eupLooping = false;
if (_vm->_game.version == 3) {
- _soundOverride = new SoundOvrParameters[200];
- memset(_soundOverride, 0, 200 * sizeof(SoundOvrParameters));
- }
-
- _eupLooping = false;
- _unkFlags = 0x33;
+ _soundOverride = new SoundOvrParameters[_numSoundMax];
+ memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
+ }
_driver = new TownsEuphonyDriver(mixer);
}
-Player_Towns::~Player_Towns() {
- delete[] _soundOverride;
+Player_Towns_v1::~Player_Towns_v1() {
delete _driver;
+ delete[] _soundOverride;
}
-bool Player_Towns::init() {
+bool Player_Towns_v1::init() {
if (!_driver)
return false;
@@ -63,47 +214,53 @@ bool Player_Towns::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.
- _driver->intf()->setSoundEffectChanMask(-1);
+ _intf->setSoundEffectChanMask(-1);
setVolumeCD(255, 255);
return true;
}
-void Player_Towns::setMusicVolume(int vol) {
+void Player_Towns_v1::setMusicVolume(int vol) {
_driver->setMusicVolume(vol);
}
-void Player_Towns::setSfxVolume(int vol) {
- _driver->setSoundEffectVolume(vol);
-}
-
-void Player_Towns::startSound(int sound) {
+void Player_Towns_v1::startSound(int sound) {
uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
- if (_vm->_game.version != 3) {
+ if (_vm->_game.version != 3)
ptr += 2;
- } else if (_soundOverride && sound > 0 && sound < 200) {
- memcpy(&_ovrCur, &_soundOverride[sound], sizeof(SoundOvrParameters));
- memset(&_soundOverride[sound], 0, sizeof(SoundOvrParameters));
- }
int type = ptr[13];
if (type == 0) {
- playPcmTrack(sound, ptr + 6);
+ 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);
}
- memset(&_ovrCur, 0, sizeof(SoundOvrParameters));
+
+ if (_vm->_game.version == 3)
+ _soundOverride[sound].vLeft = _soundOverride[sound].vRight = _soundOverride[sound].note = 0;
}
-void Player_Towns::stopSound(int sound) {
+void Player_Towns_v1::stopSound(int sound) {
if (sound == 0 || sound == _cdaCurrentSound) {
_cdaCurrentSound = 0;
_vm->_sound->stopCD();
@@ -119,7 +276,7 @@ void Player_Towns::stopSound(int sound) {
stopPcmTrack(sound);
}
-void Player_Towns::stopAllSounds() {
+void Player_Towns_v1::stopAllSounds() {
_cdaCurrentSound = 0;
_vm->_sound->stopCD();
_vm->_sound->stopCDTimer();
@@ -131,19 +288,15 @@ void Player_Towns::stopAllSounds() {
stopPcmTrack(0);
}
-int Player_Towns::getSoundStatus(int sound) const {
+int Player_Towns_v1::getSoundStatus(int sound) const {
if (sound == _cdaCurrentSound)
return _vm->_sound->pollCD();
if (sound == _eupCurrentSound)
return _driver->parserIsPlaying() ? 1 : 0;
- for (int i = 1; i < 9; i++) {
- if (_pcmCurrentSound[i].index == sound)
- return _driver->soundEffectIsPlaying(i + 0x3f) ? 1 : 0;
- }
- return 0;
+ return Player_Towns::getSoundStatus(sound);
}
-int32 Player_Towns::doCommand(int numargs, int args[]) {
+int32 Player_Towns_v1::doCommand(int numargs, int args[]) {
int32 res = 0;
switch (args[0]) {
@@ -176,40 +329,40 @@ int32 Player_Towns::doCommand(int numargs, int args[]) {
break;
default:
- warning("Player_Towns::doCommand: Unknown command %d", args[0]);
+ warning("Player_Towns_v1::doCommand: Unknown command %d", args[0]);
break;
}
return res;
}
-void Player_Towns::setVolumeCD(int left, int right) {
+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::setSoundVolume(int sound, int left, int right) {
- if (_soundOverride && sound > 0 && sound < 200) {
+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::setSoundNote(int sound, int note) {
- if (_soundOverride && sound > 0 && sound < 200)
+void Player_Towns_v1::setSoundNote(int sound, int note) {
+ if (_soundOverride && sound > 0 && sound < _numSoundMax)
_soundOverride[sound].note = note;
}
-void Player_Towns::saveLoadWithSerializer(Serializer *ser) {
+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, _cdaCurrentSoundTemp, sleUint8, VER(81)),
- MKLINE(Player_Towns, _cdaNumLoopsTemp, sleUint8, VER(81)),
- MKLINE(Player_Towns, _cdaVolLeft, sleUint8, VER(81)),
- MKLINE(Player_Towns, _cdaVolRight, sleUint8, VER(81)),
+ 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()
};
@@ -219,43 +372,19 @@ void Player_Towns::saveLoadWithSerializer(Serializer *ser) {
_eupCurrentSound = 0;
static const SaveLoadEntry eupEntries[] = {
- MKLINE(Player_Towns, _eupCurrentSound, sleUint8, VER(81)),
- MKLINE(Player_Towns, _eupLooping, sleUint8, VER(81)),
- MKLINE(Player_Towns, _eupVolLeft, sleUint8, VER(81)),
- MKLINE(Player_Towns, _eupVolRight, sleUint8, VER(81)),
+ 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);
- 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 (_driver->soundEffectIsPlaying(i + 0x3f))
- continue;
-
- _driver->stopSoundEffect(i + 0x3f);
-
- _pcmCurrentSound[i].index = 0;
- }
-
- ser->saveLoadArrayOf(_pcmCurrentSound, 9, sizeof(PcmCurrentSound), pcmEntries);
+ Player_Towns::saveLoadWithSerializer(ser);
}
-void Player_Towns::restoreAfterLoad() {
+void Player_Towns_v1::restoreAfterLoad() {
setVolumeCD(_cdaVolLeft, _cdaVolRight);
if (_cdaCurrentSoundTemp) {
@@ -281,67 +410,10 @@ void Player_Towns::restoreAfterLoad() {
}
}
- for (int i = 1; i < 9; i++) {
- if (!_pcmCurrentSound[i].index)
- 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);
- }
-}
-
-int Player_Towns::getNextFreePcmChannel(int sound, int sfxChanRelIndex) {
- int chan = 0;
- for (int i = 8; i; i--) {
- if (!_pcmCurrentSound[i].index) {
- chan = i;
- continue;
- }
-
- if (_driver->soundEffectIsPlaying(i + 0x3f))
- continue;
-
- chan = i;
- _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
- }
-
- if (!chan) {
- uint16 l = 0xffff;
- uint8 *ptr = 0;
- for (int i = 8; i; i--) {
- ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index) + 6;
- uint16 a = READ_LE_UINT16(ptr + 10);
- if (a <= l) {
- chan = i;
- l = a;
- }
- }
-
- ptr = _vm->getResourceAddress(rtSound, sound) + 6;
- if (l <= READ_LE_UINT16(ptr + 10))
- _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
- else
- chan = 0;
- }
-
- if (chan) {
- _pcmCurrentSound[chan].index = sound;
- _pcmCurrentSound[chan].chan = sfxChanRelIndex;
- }
-
- return chan;
+ Player_Towns::restoreAfterLoad();
}
-void Player_Towns::restartLoopingSounds() {
+void Player_Towns_v1::restartLoopingSounds() {
if (_cdaNumLoops && !_cdaForceRestart)
_cdaForceRestart = 1;
@@ -368,7 +440,7 @@ void Player_Towns::restartLoopingSounds() {
_driver->intf()->callback(73, 1);
}
-void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) {
+void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) {
uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2;
if (pan > 99)
@@ -376,6 +448,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) {
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;
@@ -385,7 +458,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) {
pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64;
- playPcmTrack(sound, ptr + 6, velo, pan, note);
+ playPcmTrack(sound, ptr + 6, velo ? velo : ptr[14] >> 1, pan, note ? note : ptr[50], pri);
} else if (ptr[13] == 2) {
int volLeft = velo;
@@ -405,7 +478,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) {
}
}
-void Player_Towns::stopSoundSuspendLooping(int sound) {
+void Player_Towns_v1::stopSoundSuspendLooping(int sound) {
if (!sound) {
return;
} else if (sound == _cdaCurrentSound) {
@@ -426,7 +499,7 @@ void Player_Towns::stopSoundSuspendLooping(int sound) {
}
}
-void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) {
+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;
@@ -451,9 +524,9 @@ void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) {
_driver->intf()->callback(4, i, i);
}
- _eupVolLeft = _ovrCur.vLeft;
- _eupVolRight = _ovrCur.vRight;
- int lvl = _ovrCur.vLeft + _ovrCur.vRight;
+ _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;
@@ -474,58 +547,21 @@ void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) {
_eupCurrentSound = sound;
}
-void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note) {
- const uint8 *ptr = data;
- const uint8 *sfxData = ptr + 16;
-
- int note2, velocity;
-
- if (velo)
- velocity = velo;
- else if (_ovrCur.vLeft + _ovrCur.vRight)
- velocity = (_ovrCur.vLeft + _ovrCur.vRight) >> 2;
- else
- velocity = ptr[8] >> 1;
-
- int numChan = ptr[14];
- for (int i = 0; i < numChan; i++) {
- int chan = getNextFreePcmChannel(sound, i);
- if (!chan)
- return;
-
- _driver->intf()->callback(70, _unkFlags);
- _driver->chanPanPos(chan + 0x3f, pan);
-
- if (note)
- note2 = note;
- else if (_ovrCur.note)
- note2 = _ovrCur.note;
- else
- note2 = sfxData[28];
-
- _driver->playSoundEffect(chan + 0x3f, note2, velocity, sfxData);
-
- _pcmCurrentSound[chan].note = note2;
- _pcmCurrentSound[chan].velo = velocity;
- _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::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) {
+void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) {
const uint8 *ptr = data;
if (!sound)
return;
if (!skipTrackVelo) {
- if (_ovrCur.vLeft + _ovrCur.vRight)
- setVolumeCD(_ovrCur.vLeft, _ovrCur.vRight);
- else
+ 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)
@@ -543,14 +579,170 @@ void Player_Towns::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo
_cdaCurrentSound = sound;
}
-void Player_Towns::stopPcmTrack(int sound) {
- for (int i = 1; i < 9; i++) {
- if (sound == _pcmCurrentSound[i].index || !sound) {
- _driver->stopSoundEffect(i + 0x3f);
- _pcmCurrentSound[i].index = 0;
+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;
}
-} // End of namespace Scumm
+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
index 6d87c93c09..e5023d25c2 100644
--- a/engines/scumm/player_towns.h
+++ b/engines/scumm/player_towns.h
@@ -27,20 +27,68 @@
#define SCUMM_PLAYER_TOWNS_H
#include "scumm/scumm.h"
-#include "scumm/music.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, Audio::Mixer *mixer);
- virtual ~Player_Towns();
+ 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 setSfxVolume(int vol);
void startSound(int sound);
void stopSound(int sound);
void stopAllSounds();
@@ -49,7 +97,7 @@ public:
int getCurrentCdaSound() { return _cdaCurrentSound; }
int getCurrentCdaVolume() { return (_cdaVolLeft + _cdaVolRight + 1) >> 1; }
- virtual int32 doCommand(int numargs, int args[]);
+ int32 doCommand(int numargs, int args[]);
void setVolumeCD(int left, int right);
void setSoundVolume(int sound, int left, int right);
@@ -60,23 +108,14 @@ public:
TownsEuphonyDriver *driver() { return _driver; }
-protected:
- virtual int getNextFreePcmChannel(int sound, int sfxChanRelIndex);
-
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 playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0);
void playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo = false);
- void stopPcmTrack(int sound);
-
- uint8 _cdaVolLeft;
- uint8 _cdaVolRight;
-
struct SoundOvrParameters {
uint8 vLeft;
uint8 vRight;
@@ -84,21 +123,10 @@ private:
};
SoundOvrParameters *_soundOverride;
- SoundOvrParameters _ovrCur;
-
- uint8 _unkFlags;
-
- struct PcmCurrentSound {
- uint16 index;
- uint16 chan;
- uint8 note;
- uint8 velo;
- uint8 pan;
- uint8 paused;
- uint8 looping;
- uint32 priority;
- } _pcmCurrentSound[9];
+ uint8 _cdaVolLeft;
+ uint8 _cdaVolRight;
+
uint8 _eupCurrentSound;
uint8 _eupLooping;
uint8 _eupVolLeft;
@@ -112,7 +140,40 @@ private:
uint8 _cdaNumLoopsTemp;
TownsEuphonyDriver *_driver;
- ScummEngine *_vm;
+};
+
+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
diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/player_v2.cpp
index c1c9b3930e..60103b20fb 100644
--- a/engines/scumm/player_v2.cpp
+++ b/engines/scumm/player_v2.cpp
@@ -23,8 +23,6 @@
*
*/
-
-#include "engines/engine.h"
#include "scumm/player_v2.h"
#include "scumm/scumm.h"
#include "sound/mididrv.h"
@@ -32,373 +30,51 @@
namespace Scumm {
-#define FREQ_HZ 236 // Don't change!
-
#define SPK_DECAY 0xa000 /* Depends on sample rate */
#define PCJR_DECAY 0xa000 /* Depends on sample rate */
-#define FIXP_SHIFT 16
-#define MAX_OUTPUT 0x7fff
-
#define NG_PRESET 0x0f35 /* noise generator preset */
#define FB_WNOISE 0x12000 /* feedback for white noise */
#define FB_PNOISE 0x08000 /* feedback for periodic noise */
-const uint8 note_lengths[] = {
- 0,
- 0, 0, 2,
- 0, 3, 4,
- 5, 6, 8,
- 9, 12, 16,
- 18, 24, 32,
- 36, 48, 64,
- 72, 96
-};
-
-static const uint16 hull_offsets[] = {
- 0, 12, 24, 36, 48, 60,
- 72, 88, 104, 120, 136, 256,
- 152, 164, 180
-};
-
-static const int16 hulls[] = {
- // hull 0
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, -1, 0, 0,
- // hull 1 (staccato)
- 3, -1, 0, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 2 (legato)
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,
- // hull 3 (staccatissimo)
- 3, -1, 0, 2, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 4
- 3, -1, 0, 6, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 5
- 3, -1, 0, 16, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 6
- (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
- (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 7
- (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
- 28000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 8
- (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
- 28000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 9
- (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
- (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
- // hull 10
- (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
- (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 12
- 0, -1, 150, 340, -150, 340, 0, -1,
- 0, -1, 0, 0,
- // hull 13 == 164
- 20000, -1, 4000, 7, 1000, 15, 0, 0,
- (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
-
- // hull 14 == 180
- (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
- (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
-
- // hull misc = 196
- (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- // hull 11 == 256
- (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
- 0, -1, 0, 0
-};
-
-static const uint16 freqmod_lengths[] = {
- 0x1000, 0x1000, 0x20, 0x2000, 0x1000
-};
-
-static const uint16 freqmod_offsets[] = {
- 0, 0x100, 0x200, 0x302, 0x202
-};
-
-static const int8 freqmod_table[0x502] = {
- 0, 3, 6, 9, 12, 15, 18, 21,
- 24, 27, 30, 33, 36, 39, 42, 45,
- 48, 51, 54, 57, 59, 62, 65, 67,
- 70, 73, 75, 78, 80, 82, 85, 87,
- 89, 91, 94, 96, 98, 100, 102, 103,
- 105, 107, 108, 110, 112, 113, 114, 116,
- 117, 118, 119, 120, 121, 122, 123, 123,
- 124, 125, 125, 126, 126, 126, 126, 126,
- 126, 126, 126, 126, 126, 126, 125, 125,
- 124, 123, 123, 122, 121, 120, 119, 118,
- 117, 116, 114, 113, 112, 110, 108, 107,
- 105, 103, 102, 100, 98, 96, 94, 91,
- 89, 87, 85, 82, 80, 78, 75, 73,
- 70, 67, 65, 62, 59, 57, 54, 51,
- 48, 45, 42, 39, 36, 33, 30, 27,
- 24, 21, 18, 15, 12, 9, 6, 3,
- 0, -3, -6, -9, -12, -15, -18, -21,
- -24, -27, -30, -33, -36, -39, -42, -45,
- -48, -51, -54, -57, -59, -62, -65, -67,
- -70, -73, -75, -78, -80, -82, -85, -87,
- -89, -91, -94, -96, -98,-100,-102,-103,
- -105,-107,-108,-110,-112,-113,-114,-116,
- -117,-118,-119,-120,-121,-122,-123,-123,
- -124,-125,-125,-126,-126,-126,-126,-126,
- -126,-126,-126,-126,-126,-126,-125,-125,
- -124,-123,-123,-122,-121,-120,-119,-118,
- -117,-116,-114,-113,-112,-110,-108,-107,
- -105,-103,-102,-100, -98, -96, -94, -91,
- -89, -87, -85, -82, -80, -78, -75, -73,
- -70, -67, -65, -62, -59, -57, -54, -51,
- -48, -45, -42, -39, -36, -33, -30, -27,
- -24, -21, -18, -15, -12, -9, -6, -3,
-
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127,
- -128,-127,-126,-125,-124,-123,-122,-121,
- -120,-119,-118,-117,-116,-115,-114,-113,
- -112,-111,-110,-109,-108,-107,-106,-105,
- -104,-103,-102,-101,-100, -99, -98, -97,
- -96, -95, -94, -93, -92, -91, -90, -89,
- -88, -87, -86, -85, -84, -83, -82, -81,
- -80, -79, -78, -77, -76, -75, -74, -73,
- -72, -71, -70, -69, -68, -67, -66, -65,
- -64, -63, -62, -61, -60, -59, -58, -57,
- -56, -55, -54, -53, -52, -51, -50, -49,
- -48, -47, -46, -45, -44, -43, -42, -41,
- -40, -39, -38, -37, -36, -35, -34, -33,
- -32, -31, -30, -29, -28, -27, -26, -25,
- -24, -23, -22, -21, -20, -19, -18, -17,
- -16, -15, -14, -13, -12, -11, -10, -9,
- -8, -7, -6, -5, -4, -3, -2, -1,
-
- -120, 120,
-
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
-
- 41, 35, -66,-124, -31, 108, -42, -82,
- 82,-112, 73, -15, -15, -69, -23, -21,
- -77, -90, -37, 60,-121, 12, 62,-103,
- 36, 94, 13, 28, 6, -73, 71, -34,
- -77, 18, 77, -56, 67, -69,-117, -90,
- 31, 3, 90, 125, 9, 56, 37, 31,
- 93, -44, -53, -4,-106, -11, 69, 59,
- 19, 13,-119, 10, 28, -37, -82, 50,
- 32,-102, 80, -18, 64, 120, 54, -3,
- 18, 73, 50, -10, -98, 125, 73, -36,
- -83, 79, 20, -14, 68, 64, 102, -48,
- 107, -60, 48, -73, 50, 59, -95, 34,
- -10, 34,-111, -99, -31,-117, 31, -38,
- -80, -54,-103, 2, -71, 114, -99, 73,
- 44,-128, 126, -59,-103, -43, -23,-128,
- -78, -22, -55, -52, 83, -65, 103, -42,
- -65, 20, -42, 126, 45, -36,-114, 102,
- -125, -17, 87, 73, 97, -1, 105,-113,
- 97, -51, -47, 30, -99,-100, 22, 114,
- 114, -26, 29, -16,-124, 79, 74, 119,
- 2, -41, -24, 57, 44, 83, -53, -55,
- 18, 30, 51, 116, -98, 12, -12, -43,
- -44, -97, -44, -92, 89, 126, 53, -49,
- 50, 34, -12, -52, -49, -45,-112, 45,
- 72, -45,-113, 117, -26, -39, 29, 42,
- -27, -64, -9, 43, 120,-127,-121, 68,
- 14, 95, 80, 0, -44, 97,-115, -66,
- 123, 5, 21, 7, 59, 51,-126, 31,
- 24, 112,-110, -38, 100, 84, -50, -79,
- -123, 62, 105, 21, -8, 70, 106, 4,
- -106, 115, 14, -39, 22, 47, 103, 104,
- -44, -9, 74, 74, -48, 87, 104, 118,
- -6, 22, -69, 17, -83, -82, 36,-120,
- 121, -2, 82, -37, 37, 67, -27, 60,
- -12, 69, -45, -40, 40, -50, 11, -11,
- -59, 96, 89, 61,-105, 39,-118, 89,
- 118, 45, -48, -62, -55, -51, 104, -44,
- 73, 106, 121, 37, 8, 97, 64, 20,
- -79, 59, 106, -91, 17, 40, -63,-116,
- -42, -87, 11,-121,-105,-116, 47, -15,
- 21, 29,-102,-107, -63,-101, -31, -64,
- 126, -23, -88,-102, -89,-122, -62, -75,
- 84, -65,-102, -25, -39, 35, -47, 85,
- -112, 56, 40, -47, -39, 108, -95, 102,
- 94, 78, -31, 48,-100, -2, -39, 113,
- -97, -30, -91, -30, 12,-101, -76, 71,
- 101, 56, 42, 70,-119, -87,-126, 121,
- 122, 118, 120, -62, 99, -79, 38, -33,
- -38, 41, 109, 62, 98, -32,-106, 18,
- 52, -65, 57, -90, 63,-119, 94, -15,
- 109, 14, -29, 108, 40, -95, 30, 32,
- 29, -53, -62, 3, 63, 65, 7,-124,
- 15, 20, 5, 101, 27, 40, 97, -55,
- -59, -25, 44,-114, 70, 54, 8, -36,
- -13, -88,-115, -2, -66, -14, -21, 113,
- -1, -96, -48, 59, 117, 6,-116, 126,
- -121, 120, 115, 77, -48, -66,-126, -66,
- -37, -62, 70, 65, 43,-116, -6, 48,
- 127, 112, -16, -89, 84,-122, 50,-107,
- -86, 91, 104, 19, 11, -26, -4, -11,
- -54, -66, 125, -97,-119,-118, 65, 27,
- -3, -72, 79, 104, -10, 114, 123, 20,
- -103, -51, -45, 13, -16, 68, 58, -76,
- -90, 102, 83, 51, 11, -53, -95, 16
-};
-
-
-static const uint16 spk_freq_table[12] = {
- 36484, 34436, 32503, 30679, 28957, 27332,
- 25798, 24350, 22983, 21693, 20476, 19326
-};
-
-static const uint16 pcjr_freq_table[12] = {
- 65472, 61760, 58304, 55040, 52032, 49024,
- 46272, 43648, 41216, 38912, 36736, 34624
-};
-
-
-Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr) {
- int i;
-
- _isV3Game = (scumm->_game.version >= 3);
- _vm = scumm;
- _mixer = mixer;
- _sampleRate = _mixer->getOutputRate();
-
- _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
- // Initialize sound queue
- _current_nr = _next_nr = 0;
- _current_data = _next_data = 0;
+Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
+ : Player_V2Base(scumm, mixer, pcjr) {
- // Initialize channel code
- for (i = 0; i < 4; ++i)
- clear_channel(i);
-
- _next_tick = 0;
- _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
-
- // Initialize V3 music timer
- _music_timer_ctr = _music_timer = 0;
- _ticks_per_music_timer = 65535;
+ int i;
// Initialize square generator
_level = 0;
_RNG = NG_PRESET;
- set_pcjr(pcjr);
- setMusicVolume(255);
-
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-}
-
-Player_V2::~Player_V2() {
- Common::StackLock lock(_mutex);
- _mixer->stopHandle(_soundHandle);
-}
-
-void Player_V2::set_pcjr(bool pcjr) {
- Common::StackLock lock(_mutex);
-
_pcjr = pcjr;
if (_pcjr) {
_decay = PCJR_DECAY;
_update_step = (_sampleRate << FIXP_SHIFT) / (111860 * 2);
- _freqs_table = pcjr_freq_table;
} else {
_decay = SPK_DECAY;
_update_step = (_sampleRate << FIXP_SHIFT) / (1193000 * 2);
- _freqs_table = spk_freq_table;
}
- /* adapt _decay to sample rate. It must be squared when
- * sample rate doubles.
- */
- int i;
+ // Adapt _decay to sample rate. It must be squared when
+ // sample rate doubles.
for (i = 0; (_sampleRate << i) < 30000; i++)
_decay = _decay * _decay / 65536;
_timer_output = 0;
for (i = 0; i < 4; i++)
_timer_count[i] = 0;
+
+ setMusicVolume(255);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+Player_V2::~Player_V2() {
+ Common::StackLock lock(_mutex);
+ _mixer->stopHandle(_soundHandle);
}
void Player_V2::setMusicVolume (int vol) {
@@ -421,33 +97,6 @@ void Player_V2::setMusicVolume (int vol) {
_volumetable[15] = 0;
}
-void Player_V2::chainSound(int nr, byte *data) {
- int offset = _header_len + (_pcjr ? 10 : 2);
-
- _current_nr = nr;
- _current_data = data;
-
- for (int i = 0; i < 4; i++) {
- clear_channel(i);
-
- _channels[i].d.music_script_nr = nr;
- if (data) {
- _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
- if (_channels[i].d.next_cmd)
- _channels[i].d.time_left = 1;
- }
- }
- _music_timer = 0;
-}
-
-void Player_V2::chainNextSound() {
- if (_next_nr) {
- chainSound(_next_nr, _next_data);
- _next_nr = 0;
- _next_data = 0;
- }
-}
-
void Player_V2::stopAllSounds() {
Common::StackLock lock(_mutex);
@@ -476,11 +125,11 @@ void Player_V2::stopSound(int nr) {
}
void Player_V2::startSound(int nr) {
+ Common::StackLock lock(_mutex);
+
byte *data = _vm->getResourceAddress(rtSound, nr);
assert(data);
- Common::StackLock lock(_mutex);
-
int cprio = _current_data ? *(_current_data + _header_len) : 0;
int prio = *(data + _header_len);
int nprio = _next_data ? *(_next_data + _header_len) : 0;
@@ -519,272 +168,11 @@ int Player_V2::getSoundStatus(int nr) const {
return _current_nr == nr || _next_nr == nr;
}
-
-void Player_V2::clear_channel(int i) {
- ChannelInfo *channel = &_channels[i];
- memset(channel, 0, sizeof(ChannelInfo));
-}
-
-int Player_V2::getMusicTimer() {
- if (_isV3Game)
- return _music_timer;
- else
- return _channels[0].d.music_timer;
-}
-
-void Player_V2::execute_cmd(ChannelInfo *channel) {
- uint16 value;
- int16 offset;
- uint8 *script_ptr;
- ChannelInfo * current_channel;
- ChannelInfo * dest_channel;
-
- current_channel = channel;
-
- if (channel->d.next_cmd == 0)
- goto check_stopped;
- script_ptr = &_current_data[channel->d.next_cmd];
-
- for (;;) {
- uint8 opcode = *script_ptr++;
- if (opcode >= 0xf8) {
- switch (opcode) {
- case 0xf8: // set hull curve
- debug(7, "channels[%lu]: hull curve %2d",
- (long)(channel - _channels), *script_ptr);
- channel->d.hull_curve = hull_offsets[*script_ptr / 2];
- script_ptr++;
- break;
-
- case 0xf9: // set freqmod curve
- debug(7, "channels[%lu]: freqmod curve %2d",
- (long)(channel - _channels), *script_ptr);
- channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
- channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
- script_ptr++;
- break;
-
- case 0xfd: // clear other channel
- value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
- debug(7, "clear channel %d", value);
- script_ptr += 2;
- // In Indy3, when traveling to Venice a command is
- // issued to clear channel 4. So we introduce a 4th
- // channel, which is never used. All OOB accesses are
- // mapped to this channel.
- //
- // The original game had room for 8 channels, but only
- // channels 0-3 are read, changes to other channels
- // had no effect.
- if (value >= ARRAYSIZE (_channels))
- value = 4;
- channel = &_channels[value];
- // fall through
-
- case 0xfa: // clear current channel
- if (opcode == 0xfa)
- debug(7, "clear channel");
- channel->d.next_cmd = 0;
- channel->d.base_freq = 0;
- channel->d.freq_delta = 0;
- channel->d.freq = 0;
- channel->d.volume = 0;
- channel->d.volume_delta = 0;
- channel->d.inter_note_pause = 0;
- channel->d.transpose = 0;
- channel->d.hull_curve = 0;
- channel->d.hull_offset = 0;
- channel->d.hull_counter = 0;
- channel->d.freqmod_table = 0;
- channel->d.freqmod_offset = 0;
- channel->d.freqmod_incr = 0;
- channel->d.freqmod_multiplier = 0;
- channel->d.freqmod_modulo = 0;
- break;
-
- case 0xfb: // ret from subroutine
- debug(7, "ret from sub");
- script_ptr = _retaddr;
- break;
-
- case 0xfc: // call subroutine
- offset = READ_LE_UINT16 (script_ptr);
- debug(7, "subroutine %d", offset);
- script_ptr += 2;
- _retaddr = script_ptr;
- script_ptr = _current_data + offset;
- break;
-
- case 0xfe: // loop music
- opcode = *script_ptr++;
- offset = READ_LE_UINT16 (script_ptr);
- script_ptr += 2;
- debug(7, "loop if %d to %d", opcode, offset);
- if (!channel->array[opcode / 2] || --channel->array[opcode/2])
- script_ptr += offset;
- break;
-
- case 0xff: // set parameter
- opcode = *script_ptr++;
- value = READ_LE_UINT16 (script_ptr);
- channel->array[opcode / 2] = value;
- debug(7, "channels[%lu]: set param %2d = %5d",
- (long)(channel - _channels), opcode, value);
- script_ptr += 2;
- if (opcode == 14) {
- /* tempo var */
- _ticks_per_music_timer = 125;
- }
- if (opcode == 0)
- goto end;
- break;
- }
- } else { // opcode < 0xf8
- for (;;) {
- int16 note, octave;
- int is_last_note;
- dest_channel = &_channels[(opcode >> 5) & 3];
-
- if (!(opcode & 0x80)) {
-
- int tempo = channel->d.tempo;
- if (!tempo)
- tempo = 1;
- channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
-
- note = *script_ptr++;
- is_last_note = note & 0x80;
- note &= 0x7f;
- if (note == 0x7f) {
- debug(8, "channels[%lu]: pause %d",
- (long)(channel - _channels), channel->d.time_left);
- goto end;
- }
- } else {
-
- channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
-
- if ((opcode & 0x10)) {
- debug(8, "channels[%lu]: pause %d",
- (long)(channel - _channels), channel->d.time_left);
- goto end;
- }
-
- is_last_note = 0;
- note = (*script_ptr++) & 0x7f;
- }
-
- debug(8, "channels[%lu]: @%04lx note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
- (long)(dest_channel - channel), (long)(script_ptr ? script_ptr - _current_data - 2 : 0),
- note, (signed short) dest_channel->d.transpose, channel->d.time_left,
- dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
- dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
- is_last_note ? "last":"");
-
- uint16 myfreq;
- dest_channel->d.time_left = channel->d.time_left;
- dest_channel->d.note_length =
- channel->d.time_left - dest_channel->d.inter_note_pause;
- note += dest_channel->d.transpose;
- while (note < 0)
- note += 12;
- octave = note / 12;
- note = note % 12;
- dest_channel->d.hull_offset = 0;
- dest_channel->d.hull_counter = 1;
- if (_pcjr && dest_channel == &_channels[3]) {
- dest_channel->d.hull_curve = 196 + note * 12;
- myfreq = 384 - 64 * octave;
- } else {
- myfreq = _freqs_table[note] >> octave;
- }
- dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
- if (is_last_note)
- goto end;
- opcode = *script_ptr++;
- }
- }
- }
-
-end:
- channel = current_channel;
- if (channel->d.time_left) {
- channel->d.next_cmd = script_ptr - _current_data;
- return;
- }
-
- channel->d.next_cmd = 0;
-
-check_stopped:
- int i;
- for (i = 0; i < 4; i++) {
- if (_channels[i].d.time_left)
- return;
- }
-
- _current_nr = 0;
- _current_data = 0;
- chainNextSound();
- return;
-}
-
-void Player_V2::next_freqs(ChannelInfo *channel) {
- channel->d.volume += channel->d.volume_delta;
- channel->d.base_freq += channel->d.freq_delta;
-
- channel->d.freqmod_offset += channel->d.freqmod_incr;
- if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
- channel->d.freqmod_offset -= channel->d.freqmod_modulo;
- channel->d.freq =
- (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
- * (int) channel->d.freqmod_multiplier / 256
- + channel->d.base_freq;
-
- debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
- channel->d.base_freq, (int16)channel->d.freq_delta,
- channel->d.freqmod_table, channel->d.freqmod_offset,
- channel->d.freqmod_incr, channel->d.freqmod_multiplier,
- channel->d.freq);
-
- if (channel->d.note_length && !--channel->d.note_length) {
- channel->d.hull_offset = 16;
- channel->d.hull_counter = 1;
- }
-
- if (!--channel->d.time_left) {
- execute_cmd(channel);
- }
-
-#if 0
- debug(9, "channels[%d]: freq %d hull %d/%d/%d",
- channel - &_channels[0], channel->d.freq,
- channel->d.hull_curve, channel->d.hull_offset,
- channel->d.hull_counter);
-#endif
-
- if (channel->d.hull_counter && !--channel->d.hull_counter) {
- for (;;) {
- const int16 *hull_ptr = hulls
- + channel->d.hull_curve + channel->d.hull_offset / 2;
- if (hull_ptr[1] == -1) {
- channel->d.volume = hull_ptr[0];
- if (hull_ptr[0] == 0)
- channel->d.volume_delta = 0;
- channel->d.hull_offset += 4;
- } else {
- channel->d.volume_delta = hull_ptr[0];
- channel->d.hull_counter = hull_ptr[1];
- channel->d.hull_offset += 4;
- break;
- }
- }
- }
-}
-
-void Player_V2::do_mix(int16 *data, uint len) {
+int Player_V2::readBuffer(int16 *data, const int numSamples) {
Common::StackLock lock(_mutex);
uint step;
+ uint len = numSamples / 2;
do {
if (!(_next_tick >> FIXP_SHIFT)) {
@@ -802,18 +190,8 @@ void Player_V2::do_mix(int16 *data, uint len) {
data += 2 * step;
_next_tick -= step << FIXP_SHIFT;
} while (len -= step);
-}
-void Player_V2::nextTick() {
- for (int i = 0; i < 4; i++) {
- if (!_channels[i].d.time_left)
- continue;
- next_freqs(&_channels[i]);
- }
- if (_music_timer_ctr++ >= _ticks_per_music_timer) {
- _music_timer_ctr = 0;
- _music_timer++;
- }
+ return numSamples;
}
void Player_V2::lowPassFilter(int16 *sample, uint len) {
diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h
index 2c3e784928..6a0b3d6d5e 100644
--- a/engines/scumm/player_v2.h
+++ b/engines/scumm/player_v2.h
@@ -26,84 +26,35 @@
#ifndef SCUMM_PLAYER_V2_H
#define SCUMM_PLAYER_V2_H
-#include "common/scummsys.h"
-#include "common/mutex.h"
-#include "scumm/music.h"
-#include "sound/audiostream.h"
-#include "sound/mixer.h"
+#include "scumm/player_v2base.h"
namespace Scumm {
-class ScummEngine;
-
-#include "common/pack-start.h" // START STRUCT PACKING
-
-struct channel_data {
- uint16 time_left; // 00
- uint16 next_cmd; // 02
- uint16 base_freq; // 04
- uint16 freq_delta; // 06
- uint16 freq; // 08
- uint16 volume; // 10
- uint16 volume_delta; // 12
- uint16 tempo; // 14
- uint16 inter_note_pause; // 16
- uint16 transpose; // 18
- uint16 note_length; // 20
- uint16 hull_curve; // 22
- uint16 hull_offset; // 24
- uint16 hull_counter; // 26
- uint16 freqmod_table; // 28
- uint16 freqmod_offset; // 30
- uint16 freqmod_incr; // 32
- uint16 freqmod_multiplier; // 34
- uint16 freqmod_modulo; // 36
- uint16 unknown[4]; // 38 - 44
- uint16 music_timer; // 46
- uint16 music_script_nr; // 48
-} PACKED_STRUCT;
-
-#include "common/pack-end.h" // END STRUCT PACKING
-
-
/**
* Scumm V2 PC-Speaker MIDI driver.
* This simulates the pc speaker sound, which is driven by the 8253 (square
* wave generator) and a low-band filter.
*/
-class Player_V2 : public Audio::AudioStream, public MusicEngine {
+class Player_V2 : public Player_V2Base {
public:
Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr);
virtual ~Player_V2();
+ // MusicEngine API
virtual void setMusicVolume(int vol);
virtual void startSound(int sound);
virtual void stopSound(int sound);
virtual void stopAllSounds();
- virtual int getMusicTimer();
+// virtual int getMusicTimer();
virtual int getSoundStatus(int sound) const;
// AudioStream API
- int readBuffer(int16 *buffer, const int numSamples) {
- do_mix(buffer, numSamples / 2);
- return numSamples;
- }
+ int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return true; }
bool endOfData() const { return false; }
int getRate() const { return _sampleRate; }
protected:
- bool _isV3Game;
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- ScummEngine *_vm;
-
- bool _pcjr;
- int _header_len;
-
- uint32 _sampleRate;
- uint32 _next_tick;
- uint32 _tick_len;
unsigned int _update_step;
unsigned int _decay;
int _level;
@@ -113,218 +64,13 @@ protected:
int _timer_count[4];
int _timer_output;
- int _current_nr;
- byte *_current_data;
- int _next_nr;
- byte *_next_data;
- byte *_retaddr;
-
- Common::Mutex _mutex;
-
-private:
- union ChannelInfo {
- channel_data d;
- uint16 array[sizeof(channel_data)/2];
- };
-
- int _music_timer;
- int _music_timer_ctr;
- int _ticks_per_music_timer;
-
- const uint16 *_freqs_table;
-
- ChannelInfo _channels[5];
-
protected:
- virtual void nextTick();
- virtual void clear_channel(int i);
- virtual void chainSound(int nr, byte *data);
- virtual void chainNextSound();
-
virtual void generateSpkSamples(int16 *data, uint len);
virtual void generatePCjrSamples(int16 *data, uint len);
void lowPassFilter(int16 *data, uint len);
void squareGenerator(int channel, int freq, int vol,
int noiseFeedback, int16 *sample, uint len);
-
-private:
- void do_mix(int16 *buf, uint len);
-
- void set_pcjr(bool pcjr);
- void execute_cmd(ChannelInfo *channel);
- void next_freqs(ChannelInfo *channel);
-};
-
-/**
- * Scumm V2 CMS/Gameblaster MIDI driver.
- */
-class Player_V2CMS : public Audio::AudioStream, public MusicEngine {
-public:
- Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer);
- virtual ~Player_V2CMS();
-
- virtual void setMusicVolume(int vol);
- virtual void startSound(int sound);
- virtual void stopSound(int sound);
- virtual void stopAllSounds();
- virtual int getMusicTimer();
- virtual int getSoundStatus(int sound) const;
-
- // AudioStream API
- int readBuffer(int16 *buffer, const int numSamples);
- bool isStereo() const { return true; }
- bool endOfData() const { return false; }
- int getRate() const { return _sampleRate; }
-
-protected:
-
-#include "common/pack-start.h" // START STRUCT PACKING
- struct Voice {
- byte attack;
- byte decay;
- byte sustain;
- byte release;
- byte octadd;
- int16 vibrato;
- int16 vibrato2;
- int16 noise;
- } PACKED_STRUCT;
-
- struct Voice2 {
- byte *amplitudeOutput;
- byte *freqOutput;
- byte *octaveOutput;
-
- uint8 channel;
- int8 sustainLevel;
- int8 attackRate;
- uint8 maxAmpl;
- int8 decayRate;
- int8 sustainRate;
- int8 releaseRate;
- int8 releaseTime;
- int8 vibratoRate;
- int8 vibratoDepth;
-
- int8 curVibratoRate;
- int8 curVibratoUnk;
-
- int8 unkVibratoRate;
- int8 unkVibratoDepth;
-
- int8 unkRate;
- int8 unkCount;
-
- int nextProcessState;
- int8 curVolume;
- int8 curOctave;
- int8 curFreq;
-
- int8 octaveAdd;
-
- int8 playingNote;
- Voice2 *nextVoice;
-
- byte chanNumber;
- } PACKED_STRUCT;
-
- struct MusicChip {
- byte ampl[4];
- byte freq[4];
- byte octave[2];
- } PACKED_STRUCT;
-#include "common/pack-end.h" // END STRUCT PACKING
-
- Voice _cmsVoicesBase[16];
- Voice2 _cmsVoices[8];
- MusicChip _cmsChips[2];
-
- int8 _tempo;
- int8 _tempoSum;
- byte _looping;
- byte _octaveMask;
- int16 _midiDelay;
- Voice2 *_midiChannel[16];
- byte _midiChannelUse[16];
- byte *_midiData;
- byte *_midiSongBegin;
-
- int _loadedMidiSong;
-
- byte _lastMidiCommand;
- uint _outputTableReady;
- byte _clkFrequenz;
- byte _restart;
- byte _curSno;
-
- void loadMidiData(byte *data, int sound);
- void play();
-
- void processChannel(Voice2 *channel);
- void processRelease(Voice2 *channel);
- void processAttack(Voice2 *channel);
- void processDecay(Voice2 *channel);
- void processSustain(Voice2 *channel);
- void processVibrato(Voice2 *channel);
-
- void playMusicChips(const MusicChip *table);
- void playNote(byte *&data);
- void clearNote(byte *&data);
- void offAllChannels();
- void playVoice();
- void processMidiData(uint ticks);
-
- Voice2 *getFreeVoice();
- Voice2 *getPlayVoice(byte param);
-
- // from Player_V2
-protected:
- bool _isV3Game;
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- ScummEngine *_vm;
-
- int _header_len;
-
- uint32 _sampleRate;
- uint32 _next_tick;
- uint32 _tick_len;
-
- int _timer_count[4];
- int _timer_output;
-
- int _current_nr;
- byte *_current_data;
- int _next_nr;
- byte *_next_data;
- byte *_retaddr;
-
- Common::Mutex _mutex;
-
-private:
- union ChannelInfo {
- channel_data d;
- uint16 array[sizeof(channel_data)/2];
- };
-
- int _music_timer;
- int _music_timer_ctr;
- int _ticks_per_music_timer;
-
- ChannelInfo _channels[5];
-
-protected:
- virtual void nextTick();
- virtual void clear_channel(int i);
- virtual void chainSound(int nr, byte *data);
- virtual void chainNextSound();
-
-private:
- void do_mix(int16 *buf, uint len);
-
- void execute_cmd(ChannelInfo *channel);
- void next_freqs(ChannelInfo *channel);
};
} // End of namespace Scumm
diff --git a/engines/scumm/player_v2base.cpp b/engines/scumm/player_v2base.cpp
new file mode 100644
index 0000000000..61c91aae85
--- /dev/null
+++ b/engines/scumm/player_v2base.cpp
@@ -0,0 +1,657 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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/player_v2base.h"
+#include "scumm/scumm.h"
+
+#define FREQ_HZ 236 // Don't change!
+
+#define MAX_OUTPUT 0x7fff
+
+namespace Scumm {
+
+const uint8 note_lengths[] = {
+ 0,
+ 0, 0, 2,
+ 0, 3, 4,
+ 5, 6, 8,
+ 9, 12, 16,
+ 18, 24, 32,
+ 36, 48, 64,
+ 72, 96
+};
+
+static const uint16 hull_offsets[] = {
+ 0, 12, 24, 36, 48, 60,
+ 72, 88, 104, 120, 136, 256,
+ 152, 164, 180
+};
+
+static const int16 hulls[] = {
+ // hull 0
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0,
+ // hull 1 (staccato)
+ 3, -1, 0, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 2 (legato)
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // hull 3 (staccatissimo)
+ 3, -1, 0, 2, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 4
+ 3, -1, 0, 6, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 5
+ 3, -1, 0, 16, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 6
+ (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
+ (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 7
+ (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
+ 28000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 8
+ (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
+ 28000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 9
+ (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
+ (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
+ // hull 10
+ (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
+ (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 12
+ 0, -1, 150, 340, -150, 340, 0, -1,
+ 0, -1, 0, 0,
+ // hull 13 == 164
+ 20000, -1, 4000, 7, 1000, 15, 0, 0,
+ (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
+
+ // hull 14 == 180
+ (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
+ (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
+
+ // hull misc = 196
+ (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ // hull 11 == 256
+ (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0
+};
+
+static const uint16 freqmod_lengths[] = {
+ 0x1000, 0x1000, 0x20, 0x2000, 0x1000
+};
+
+static const uint16 freqmod_offsets[] = {
+ 0, 0x100, 0x200, 0x302, 0x202
+};
+
+static const int8 freqmod_table[0x502] = {
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 59, 62, 65, 67,
+ 70, 73, 75, 78, 80, 82, 85, 87,
+ 89, 91, 94, 96, 98, 100, 102, 103,
+ 105, 107, 108, 110, 112, 113, 114, 116,
+ 117, 118, 119, 120, 121, 122, 123, 123,
+ 124, 125, 125, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 125, 125,
+ 124, 123, 123, 122, 121, 120, 119, 118,
+ 117, 116, 114, 113, 112, 110, 108, 107,
+ 105, 103, 102, 100, 98, 96, 94, 91,
+ 89, 87, 85, 82, 80, 78, 75, 73,
+ 70, 67, 65, 62, 59, 57, 54, 51,
+ 48, 45, 42, 39, 36, 33, 30, 27,
+ 24, 21, 18, 15, 12, 9, 6, 3,
+ 0, -3, -6, -9, -12, -15, -18, -21,
+ -24, -27, -30, -33, -36, -39, -42, -45,
+ -48, -51, -54, -57, -59, -62, -65, -67,
+ -70, -73, -75, -78, -80, -82, -85, -87,
+ -89, -91, -94, -96, -98,-100,-102,-103,
+ -105,-107,-108,-110,-112,-113,-114,-116,
+ -117,-118,-119,-120,-121,-122,-123,-123,
+ -124,-125,-125,-126,-126,-126,-126,-126,
+ -126,-126,-126,-126,-126,-126,-125,-125,
+ -124,-123,-123,-122,-121,-120,-119,-118,
+ -117,-116,-114,-113,-112,-110,-108,-107,
+ -105,-103,-102,-100, -98, -96, -94, -91,
+ -89, -87, -85, -82, -80, -78, -75, -73,
+ -70, -67, -65, -62, -59, -57, -54, -51,
+ -48, -45, -42, -39, -36, -33, -30, -27,
+ -24, -21, -18, -15, -12, -9, -6, -3,
+
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ -128,-127,-126,-125,-124,-123,-122,-121,
+ -120,-119,-118,-117,-116,-115,-114,-113,
+ -112,-111,-110,-109,-108,-107,-106,-105,
+ -104,-103,-102,-101,-100, -99, -98, -97,
+ -96, -95, -94, -93, -92, -91, -90, -89,
+ -88, -87, -86, -85, -84, -83, -82, -81,
+ -80, -79, -78, -77, -76, -75, -74, -73,
+ -72, -71, -70, -69, -68, -67, -66, -65,
+ -64, -63, -62, -61, -60, -59, -58, -57,
+ -56, -55, -54, -53, -52, -51, -50, -49,
+ -48, -47, -46, -45, -44, -43, -42, -41,
+ -40, -39, -38, -37, -36, -35, -34, -33,
+ -32, -31, -30, -29, -28, -27, -26, -25,
+ -24, -23, -22, -21, -20, -19, -18, -17,
+ -16, -15, -14, -13, -12, -11, -10, -9,
+ -8, -7, -6, -5, -4, -3, -2, -1,
+
+ -120, 120,
+
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+
+ 41, 35, -66,-124, -31, 108, -42, -82,
+ 82,-112, 73, -15, -15, -69, -23, -21,
+ -77, -90, -37, 60,-121, 12, 62,-103,
+ 36, 94, 13, 28, 6, -73, 71, -34,
+ -77, 18, 77, -56, 67, -69,-117, -90,
+ 31, 3, 90, 125, 9, 56, 37, 31,
+ 93, -44, -53, -4,-106, -11, 69, 59,
+ 19, 13,-119, 10, 28, -37, -82, 50,
+ 32,-102, 80, -18, 64, 120, 54, -3,
+ 18, 73, 50, -10, -98, 125, 73, -36,
+ -83, 79, 20, -14, 68, 64, 102, -48,
+ 107, -60, 48, -73, 50, 59, -95, 34,
+ -10, 34,-111, -99, -31,-117, 31, -38,
+ -80, -54,-103, 2, -71, 114, -99, 73,
+ 44,-128, 126, -59,-103, -43, -23,-128,
+ -78, -22, -55, -52, 83, -65, 103, -42,
+ -65, 20, -42, 126, 45, -36,-114, 102,
+ -125, -17, 87, 73, 97, -1, 105,-113,
+ 97, -51, -47, 30, -99,-100, 22, 114,
+ 114, -26, 29, -16,-124, 79, 74, 119,
+ 2, -41, -24, 57, 44, 83, -53, -55,
+ 18, 30, 51, 116, -98, 12, -12, -43,
+ -44, -97, -44, -92, 89, 126, 53, -49,
+ 50, 34, -12, -52, -49, -45,-112, 45,
+ 72, -45,-113, 117, -26, -39, 29, 42,
+ -27, -64, -9, 43, 120,-127,-121, 68,
+ 14, 95, 80, 0, -44, 97,-115, -66,
+ 123, 5, 21, 7, 59, 51,-126, 31,
+ 24, 112,-110, -38, 100, 84, -50, -79,
+ -123, 62, 105, 21, -8, 70, 106, 4,
+ -106, 115, 14, -39, 22, 47, 103, 104,
+ -44, -9, 74, 74, -48, 87, 104, 118,
+ -6, 22, -69, 17, -83, -82, 36,-120,
+ 121, -2, 82, -37, 37, 67, -27, 60,
+ -12, 69, -45, -40, 40, -50, 11, -11,
+ -59, 96, 89, 61,-105, 39,-118, 89,
+ 118, 45, -48, -62, -55, -51, 104, -44,
+ 73, 106, 121, 37, 8, 97, 64, 20,
+ -79, 59, 106, -91, 17, 40, -63,-116,
+ -42, -87, 11,-121,-105,-116, 47, -15,
+ 21, 29,-102,-107, -63,-101, -31, -64,
+ 126, -23, -88,-102, -89,-122, -62, -75,
+ 84, -65,-102, -25, -39, 35, -47, 85,
+ -112, 56, 40, -47, -39, 108, -95, 102,
+ 94, 78, -31, 48,-100, -2, -39, 113,
+ -97, -30, -91, -30, 12,-101, -76, 71,
+ 101, 56, 42, 70,-119, -87,-126, 121,
+ 122, 118, 120, -62, 99, -79, 38, -33,
+ -38, 41, 109, 62, 98, -32,-106, 18,
+ 52, -65, 57, -90, 63,-119, 94, -15,
+ 109, 14, -29, 108, 40, -95, 30, 32,
+ 29, -53, -62, 3, 63, 65, 7,-124,
+ 15, 20, 5, 101, 27, 40, 97, -55,
+ -59, -25, 44,-114, 70, 54, 8, -36,
+ -13, -88,-115, -2, -66, -14, -21, 113,
+ -1, -96, -48, 59, 117, 6,-116, 126,
+ -121, 120, 115, 77, -48, -66,-126, -66,
+ -37, -62, 70, 65, 43,-116, -6, 48,
+ 127, 112, -16, -89, 84,-122, 50,-107,
+ -86, 91, 104, 19, 11, -26, -4, -11,
+ -54, -66, 125, -97,-119,-118, 65, 27,
+ -3, -72, 79, 104, -10, 114, 123, 20,
+ -103, -51, -45, 13, -16, 68, 58, -76,
+ -90, 102, 83, 51, 11, -53, -95, 16
+};
+
+static const uint16 spk_freq_table[12] = {
+ 36484, 34436, 32503, 30679, 28957, 27332,
+ 25798, 24350, 22983, 21693, 20476, 19326
+};
+
+static const uint16 pcjr_freq_table[12] = {
+ 65472, 61760, 58304, 55040, 52032, 49024,
+ 46272, 43648, 41216, 38912, 36736, 34624
+};
+
+
+Player_V2Base::Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr)
+ : _vm(scumm),
+ _mixer(mixer),
+ _pcjr(pcjr),
+ _sampleRate(_mixer->getOutputRate()) {
+
+ _isV3Game = (scumm->_game.version >= 3);
+
+ _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
+
+ // Initialize sound queue
+ _current_nr = _next_nr = 0;
+ _current_data = _next_data = 0;
+
+ // Initialize channel code
+ for (int i = 0; i < 4; ++i)
+ clear_channel(i);
+
+ _next_tick = 0;
+ _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
+
+ // Initialize V3 music timer
+ _music_timer_ctr = _music_timer = 0;
+ _ticks_per_music_timer = 65535;
+
+ if (_pcjr) {
+ _freqs_table = pcjr_freq_table;
+ } else {
+ _freqs_table = spk_freq_table;
+ }
+}
+
+Player_V2Base::~Player_V2Base() {
+}
+
+void Player_V2Base::chainSound(int nr, byte *data) {
+ int offset = _header_len + (_pcjr ? 10 : 2);
+
+ _current_nr = nr;
+ _current_data = data;
+
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+
+ _channels[i].d.music_script_nr = nr;
+ if (data) {
+ _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
+ if (_channels[i].d.next_cmd) {
+ _channels[i].d.time_left = 1;
+ }
+ }
+ }
+ _music_timer = 0;
+}
+
+void Player_V2Base::chainNextSound() {
+ if (_next_nr) {
+ chainSound(_next_nr, _next_data);
+ _next_nr = 0;
+ _next_data = 0;
+ }
+}
+
+// TODO: Merge stopAllSounds, stopSound() and startSound(), using some overriding
+// perhaps? Player_V2CMS's implementations start like that of Player_V2
+// but then add some MIDI related stuff.
+
+void Player_V2Base::clear_channel(int i) {
+ ChannelInfo *channel = &_channels[i];
+ memset(channel, 0, sizeof(ChannelInfo));
+}
+
+int Player_V2Base::getMusicTimer() {
+ if (_isV3Game)
+ return _music_timer;
+ else
+ return _channels[0].d.music_timer;
+}
+
+void Player_V2Base::execute_cmd(ChannelInfo *channel) {
+ uint16 value;
+ int16 offset;
+ uint8 *script_ptr;
+ ChannelInfo * current_channel;
+ ChannelInfo * dest_channel;
+
+ current_channel = channel;
+
+ if (channel->d.next_cmd == 0)
+ goto check_stopped;
+ script_ptr = &_current_data[channel->d.next_cmd];
+
+ for (;;) {
+ uint8 opcode = *script_ptr++;
+ if (opcode >= 0xf8) {
+ switch (opcode) {
+ case 0xf8: // set hull curve
+ debug(7, "channels[%d]: hull curve %2d",
+ (uint)(channel - _channels), *script_ptr);
+ channel->d.hull_curve = hull_offsets[*script_ptr / 2];
+ script_ptr++;
+ break;
+
+ case 0xf9: // set freqmod curve
+ debug(7, "channels[%d]: freqmod curve %2d",
+ (uint)(channel - _channels), *script_ptr);
+ channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
+ channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
+ script_ptr++;
+ break;
+
+ case 0xfd: // clear other channel
+ value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
+ debug(7, "clear channel %d", value);
+ script_ptr += 2;
+ // In Indy3, when traveling to Venice a command is
+ // issued to clear channel 4. So we introduce a 4th
+ // channel, which is never used. All OOB accesses are
+ // mapped to this channel.
+ //
+ // The original game had room for 8 channels, but only
+ // channels 0-3 are read, changes to other channels
+ // had no effect.
+ if (value >= ARRAYSIZE (_channels))
+ value = 4;
+ channel = &_channels[value];
+ // fall through
+
+ case 0xfa: // clear current channel
+ if (opcode == 0xfa)
+ debug(7, "clear channel");
+ channel->d.next_cmd = 0;
+ channel->d.base_freq = 0;
+ channel->d.freq_delta = 0;
+ channel->d.freq = 0;
+ channel->d.volume = 0;
+ channel->d.volume_delta = 0;
+ channel->d.inter_note_pause = 0;
+ channel->d.transpose = 0;
+ channel->d.hull_curve = 0;
+ channel->d.hull_offset = 0;
+ channel->d.hull_counter = 0;
+ channel->d.freqmod_table = 0;
+ channel->d.freqmod_offset = 0;
+ channel->d.freqmod_incr = 0;
+ channel->d.freqmod_multiplier = 0;
+ channel->d.freqmod_modulo = 0;
+ break;
+
+ case 0xfb: // ret from subroutine
+ debug(7, "ret from sub");
+ script_ptr = _retaddr;
+ break;
+
+ case 0xfc: // call subroutine
+ offset = READ_LE_UINT16 (script_ptr);
+ debug(7, "subroutine %d", offset);
+ script_ptr += 2;
+ _retaddr = script_ptr;
+ script_ptr = _current_data + offset;
+ break;
+
+ case 0xfe: // loop music
+ opcode = *script_ptr++;
+ offset = READ_LE_UINT16 (script_ptr);
+ script_ptr += 2;
+ debug(7, "loop if %d to %d", opcode, offset);
+ if (!channel->array[opcode / 2] || --channel->array[opcode/2])
+ script_ptr += offset;
+ break;
+
+ case 0xff: // set parameter
+ opcode = *script_ptr++;
+ value = READ_LE_UINT16 (script_ptr);
+ channel->array[opcode / 2] = value;
+ debug(7, "channels[%d]: set param %2d = %5d",
+ (uint)(channel - _channels), opcode, value);
+ script_ptr += 2;
+ if (opcode == 14) {
+ /* tempo var */
+ _ticks_per_music_timer = 125;
+ }
+ if (opcode == 0)
+ goto end;
+ break;
+ }
+ } else { // opcode < 0xf8
+ for (;;) {
+ int16 note, octave;
+ int is_last_note;
+ dest_channel = &_channels[(opcode >> 5) & 3];
+
+ if (!(opcode & 0x80)) {
+
+ int tempo = channel->d.tempo;
+ if (!tempo)
+ tempo = 1;
+ channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
+
+ note = *script_ptr++;
+ is_last_note = note & 0x80;
+ note &= 0x7f;
+ if (note == 0x7f) {
+ debug(8, "channels[%d]: pause %d",
+ (uint)(channel - _channels), channel->d.time_left);
+ goto end;
+ }
+ } else {
+
+ channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
+
+ if ((opcode & 0x10)) {
+ debug(8, "channels[%d]: pause %d",
+ (uint)(channel - _channels), channel->d.time_left);
+ goto end;
+ }
+
+ is_last_note = 0;
+ note = (*script_ptr++) & 0x7f;
+ }
+
+ debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
+ (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0,
+ note, (signed short) dest_channel->d.transpose, channel->d.time_left,
+ dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
+ dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
+ is_last_note ? "last":"");
+
+ uint16 myfreq;
+ dest_channel->d.time_left = channel->d.time_left;
+ dest_channel->d.note_length =
+ channel->d.time_left - dest_channel->d.inter_note_pause;
+ note += dest_channel->d.transpose;
+ while (note < 0)
+ note += 12;
+ octave = note / 12;
+ note = note % 12;
+ dest_channel->d.hull_offset = 0;
+ dest_channel->d.hull_counter = 1;
+ if (_pcjr && dest_channel == &_channels[3]) {
+ dest_channel->d.hull_curve = 196 + note * 12;
+ myfreq = 384 - 64 * octave;
+ } else {
+ myfreq = _freqs_table[note] >> octave;
+ }
+ dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
+ if (is_last_note)
+ goto end;
+ opcode = *script_ptr++;
+ }
+ }
+ }
+
+end:
+ channel = current_channel;
+ if (channel->d.time_left) {
+ channel->d.next_cmd = script_ptr - _current_data;
+ return;
+ }
+
+ channel->d.next_cmd = 0;
+
+check_stopped:
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (_channels[i].d.time_left)
+ return;
+ }
+
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+}
+
+void Player_V2Base::next_freqs(ChannelInfo *channel) {
+ channel->d.volume += channel->d.volume_delta;
+ channel->d.base_freq += channel->d.freq_delta;
+
+ channel->d.freqmod_offset += channel->d.freqmod_incr;
+ if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
+ channel->d.freqmod_offset -= channel->d.freqmod_modulo;
+
+ channel->d.freq =
+ (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
+ * (int) channel->d.freqmod_multiplier / 256
+ + channel->d.base_freq;
+
+ debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
+ channel->d.base_freq, (int16)channel->d.freq_delta,
+ channel->d.freqmod_table, channel->d.freqmod_offset,
+ channel->d.freqmod_incr, channel->d.freqmod_multiplier,
+ channel->d.freq);
+
+ if (channel->d.note_length && !--channel->d.note_length) {
+ channel->d.hull_offset = 16;
+ channel->d.hull_counter = 1;
+ }
+
+ if (!--channel->d.time_left) {
+ execute_cmd(channel);
+ }
+
+ if (channel->d.hull_counter && !--channel->d.hull_counter) {
+ for (;;) {
+ const int16 *hull_ptr = hulls
+ + channel->d.hull_curve + channel->d.hull_offset / 2;
+ if (hull_ptr[1] == -1) {
+ channel->d.volume = hull_ptr[0];
+ if (hull_ptr[0] == 0)
+ channel->d.volume_delta = 0;
+ channel->d.hull_offset += 4;
+ } else {
+ channel->d.volume_delta = hull_ptr[0];
+ channel->d.hull_counter = hull_ptr[1];
+ channel->d.hull_offset += 4;
+ break;
+ }
+ }
+ }
+}
+
+void Player_V2Base::nextTick() {
+ for (int i = 0; i < 4; i++) {
+ if (!_channels[i].d.time_left)
+ continue;
+ next_freqs(&_channels[i]);
+ }
+ if (_music_timer_ctr++ >= _ticks_per_music_timer) {
+ _music_timer_ctr = 0;
+ _music_timer++;
+ }
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_v2base.h b/engines/scumm/player_v2base.h
new file mode 100644
index 0000000000..7b90ae98cf
--- /dev/null
+++ b/engines/scumm/player_v2base.h
@@ -0,0 +1,148 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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_V2BASE_H
+#define SCUMM_PLAYER_V2BASE_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+
+#include "common/pack-start.h" // START STRUCT PACKING
+
+struct channel_data {
+ uint16 time_left; // 00
+ uint16 next_cmd; // 02
+ uint16 base_freq; // 04
+ uint16 freq_delta; // 06
+ uint16 freq; // 08
+ uint16 volume; // 10
+ uint16 volume_delta; // 12
+ uint16 tempo; // 14
+ uint16 inter_note_pause; // 16
+ uint16 transpose; // 18
+ uint16 note_length; // 20
+ uint16 hull_curve; // 22
+ uint16 hull_offset; // 24
+ uint16 hull_counter; // 26
+ uint16 freqmod_table; // 28
+ uint16 freqmod_offset; // 30
+ uint16 freqmod_incr; // 32
+ uint16 freqmod_multiplier; // 34
+ uint16 freqmod_modulo; // 36
+ uint16 unknown[4]; // 38 - 44
+ uint16 music_timer; // 46
+ uint16 music_script_nr; // 48
+} PACKED_STRUCT;
+
+#include "common/pack-end.h" // END STRUCT PACKING
+
+/**
+ * Common base class for Player_V2 and Player_V2CMS.
+ */
+class Player_V2Base : public Audio::AudioStream, public MusicEngine {
+public:
+ Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr);
+ virtual ~Player_V2Base();
+
+ // MusicEngine API
+// virtual void setMusicVolume(int vol);
+// virtual void startSound(int sound);
+// virtual void stopSound(int sound);
+// virtual void stopAllSounds();
+ virtual int getMusicTimer();
+// virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+/*
+ int readBuffer(int16 *buffer, const int numSamples) {
+ do_mix(buffer, numSamples / 2);
+ return numSamples;
+ }
+*/
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+protected:
+ enum {
+ FIXP_SHIFT = 16
+ };
+
+ bool _isV3Game;
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+ ScummEngine *_vm;
+
+ bool _pcjr;
+ int _header_len;
+
+ const uint32 _sampleRate;
+ uint32 _next_tick;
+ uint32 _tick_len;
+
+ int _current_nr;
+ byte *_current_data;
+ int _next_nr;
+ byte *_next_data;
+ byte *_retaddr;
+
+ Common::Mutex _mutex;
+
+ union ChannelInfo {
+ channel_data d;
+ uint16 array[sizeof(channel_data)/2];
+ };
+
+ ChannelInfo _channels[5];
+
+private:
+ int _music_timer;
+ int _music_timer_ctr;
+ int _ticks_per_music_timer;
+
+ const uint16 *_freqs_table;
+
+protected:
+ virtual void nextTick();
+ virtual void clear_channel(int i);
+ virtual void chainSound(int nr, byte *data);
+ virtual void chainNextSound();
+
+ void execute_cmd(ChannelInfo *channel);
+ void next_freqs(ChannelInfo *channel);
+};
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index e3e7bc1901..7bec171173 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -23,689 +23,21 @@
*
*/
-#include "engines/engine.h"
-#include "scumm/player_v2.h"
+#include "scumm/player_v2cms.h"
#include "scumm/scumm.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
+#include "sound/softsynth/cms.h"
namespace Scumm {
-#define FREQ_HZ 236 // Don't change!
-
-#define FIXP_SHIFT 16
-#define MAX_OUTPUT 0x7fff
-
-#define NG_PRESET 0x0f35 /* noise generator preset */
-#define FB_WNOISE 0x12000 /* feedback for white noise */
-#define FB_PNOISE 0x08000 /* feedback for periodic noise */
-
-// 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 -
-
-const uint8 note_lengths[] = {
- 0,
- 0, 0, 2,
- 0, 3, 4,
- 5, 6, 8,
- 9, 12, 16,
- 18, 24, 32,
- 36, 48, 64,
- 72, 96
-};
-
-static const uint16 hull_offsets[] = {
- 0, 12, 24, 36, 48, 60,
- 72, 88, 104, 120, 136, 256,
- 152, 164, 180
-};
-
-static const int16 hulls[] = {
- // hull 0
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, -1, 0, 0,
- // hull 1 (staccato)
- 3, -1, 0, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 2 (legato)
- 3, -1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0,
- // hull 3 (staccatissimo)
- 3, -1, 0, 2, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 4
- 3, -1, 0, 6, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 5
- 3, -1, 0, 16, 0, -1, 0, 0,
- 0, -1, 0, 0,
- // hull 6
- (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
- (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 7
- (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
- 28000, -1, -5000, 5, 0, -1, 0, 0,
- // hull 8
- (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
- 28000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 9
- (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
- (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
- // hull 10
- (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
- (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
- // hull 12
- 0, -1, 150, 340, -150, 340, 0, -1,
- 0, -1, 0, 0,
- // hull 13 == 164
- 20000, -1, 4000, 7, 1000, 15, 0, 0,
- (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
-
- // hull 14 == 180
- (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
- (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
-
- // hull misc = 196
- (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- // hull 11 == 256
- (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
- 0, -1, 0, 0,
-
- (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
- 0, -1, 0, 0
-};
-
-static const uint16 freqmod_lengths[] = {
- 0x1000, 0x1000, 0x20, 0x2000, 0x1000
-};
-
-static const uint16 freqmod_offsets[] = {
- 0, 0x100, 0x200, 0x302, 0x202
-};
-
-static const int8 freqmod_table[0x502] = {
- 0, 3, 6, 9, 12, 15, 18, 21,
- 24, 27, 30, 33, 36, 39, 42, 45,
- 48, 51, 54, 57, 59, 62, 65, 67,
- 70, 73, 75, 78, 80, 82, 85, 87,
- 89, 91, 94, 96, 98, 100, 102, 103,
- 105, 107, 108, 110, 112, 113, 114, 116,
- 117, 118, 119, 120, 121, 122, 123, 123,
- 124, 125, 125, 126, 126, 126, 126, 126,
- 126, 126, 126, 126, 126, 126, 125, 125,
- 124, 123, 123, 122, 121, 120, 119, 118,
- 117, 116, 114, 113, 112, 110, 108, 107,
- 105, 103, 102, 100, 98, 96, 94, 91,
- 89, 87, 85, 82, 80, 78, 75, 73,
- 70, 67, 65, 62, 59, 57, 54, 51,
- 48, 45, 42, 39, 36, 33, 30, 27,
- 24, 21, 18, 15, 12, 9, 6, 3,
- 0, -3, -6, -9, -12, -15, -18, -21,
- -24, -27, -30, -33, -36, -39, -42, -45,
- -48, -51, -54, -57, -59, -62, -65, -67,
- -70, -73, -75, -78, -80, -82, -85, -87,
- -89, -91, -94, -96, -98,-100,-102,-103,
- -105,-107,-108,-110,-112,-113,-114,-116,
- -117,-118,-119,-120,-121,-122,-123,-123,
- -124,-125,-125,-126,-126,-126,-126,-126,
- -126,-126,-126,-126,-126,-126,-125,-125,
- -124,-123,-123,-122,-121,-120,-119,-118,
- -117,-116,-114,-113,-112,-110,-108,-107,
- -105,-103,-102,-100, -98, -96, -94, -91,
- -89, -87, -85, -82, -80, -78, -75, -73,
- -70, -67, -65, -62, -59, -57, -54, -51,
- -48, -45, -42, -39, -36, -33, -30, -27,
- -24, -21, -18, -15, -12, -9, -6, -3,
-
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127,
- -128,-127,-126,-125,-124,-123,-122,-121,
- -120,-119,-118,-117,-116,-115,-114,-113,
- -112,-111,-110,-109,-108,-107,-106,-105,
- -104,-103,-102,-101,-100, -99, -98, -97,
- -96, -95, -94, -93, -92, -91, -90, -89,
- -88, -87, -86, -85, -84, -83, -82, -81,
- -80, -79, -78, -77, -76, -75, -74, -73,
- -72, -71, -70, -69, -68, -67, -66, -65,
- -64, -63, -62, -61, -60, -59, -58, -57,
- -56, -55, -54, -53, -52, -51, -50, -49,
- -48, -47, -46, -45, -44, -43, -42, -41,
- -40, -39, -38, -37, -36, -35, -34, -33,
- -32, -31, -30, -29, -28, -27, -26, -25,
- -24, -23, -22, -21, -20, -19, -18, -17,
- -16, -15, -14, -13, -12, -11, -10, -9,
- -8, -7, -6, -5, -4, -3, -2, -1,
-
- -120, 120,
-
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- -120,-120,-120,-120,-120,-120,-120,-120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
- 120, 120, 120, 120, 120, 120, 120, 120,
-
- 41, 35, -66,-124, -31, 108, -42, -82,
- 82,-112, 73, -15, -15, -69, -23, -21,
- -77, -90, -37, 60,-121, 12, 62,-103,
- 36, 94, 13, 28, 6, -73, 71, -34,
- -77, 18, 77, -56, 67, -69,-117, -90,
- 31, 3, 90, 125, 9, 56, 37, 31,
- 93, -44, -53, -4,-106, -11, 69, 59,
- 19, 13,-119, 10, 28, -37, -82, 50,
- 32,-102, 80, -18, 64, 120, 54, -3,
- 18, 73, 50, -10, -98, 125, 73, -36,
- -83, 79, 20, -14, 68, 64, 102, -48,
- 107, -60, 48, -73, 50, 59, -95, 34,
- -10, 34,-111, -99, -31,-117, 31, -38,
- -80, -54,-103, 2, -71, 114, -99, 73,
- 44,-128, 126, -59,-103, -43, -23,-128,
- -78, -22, -55, -52, 83, -65, 103, -42,
- -65, 20, -42, 126, 45, -36,-114, 102,
- -125, -17, 87, 73, 97, -1, 105,-113,
- 97, -51, -47, 30, -99,-100, 22, 114,
- 114, -26, 29, -16,-124, 79, 74, 119,
- 2, -41, -24, 57, 44, 83, -53, -55,
- 18, 30, 51, 116, -98, 12, -12, -43,
- -44, -97, -44, -92, 89, 126, 53, -49,
- 50, 34, -12, -52, -49, -45,-112, 45,
- 72, -45,-113, 117, -26, -39, 29, 42,
- -27, -64, -9, 43, 120,-127,-121, 68,
- 14, 95, 80, 0, -44, 97,-115, -66,
- 123, 5, 21, 7, 59, 51,-126, 31,
- 24, 112,-110, -38, 100, 84, -50, -79,
- -123, 62, 105, 21, -8, 70, 106, 4,
- -106, 115, 14, -39, 22, 47, 103, 104,
- -44, -9, 74, 74, -48, 87, 104, 118,
- -6, 22, -69, 17, -83, -82, 36,-120,
- 121, -2, 82, -37, 37, 67, -27, 60,
- -12, 69, -45, -40, 40, -50, 11, -11,
- -59, 96, 89, 61,-105, 39,-118, 89,
- 118, 45, -48, -62, -55, -51, 104, -44,
- 73, 106, 121, 37, 8, 97, 64, 20,
- -79, 59, 106, -91, 17, 40, -63,-116,
- -42, -87, 11,-121,-105,-116, 47, -15,
- 21, 29,-102,-107, -63,-101, -31, -64,
- 126, -23, -88,-102, -89,-122, -62, -75,
- 84, -65,-102, -25, -39, 35, -47, 85,
- -112, 56, 40, -47, -39, 108, -95, 102,
- 94, 78, -31, 48,-100, -2, -39, 113,
- -97, -30, -91, -30, 12,-101, -76, 71,
- 101, 56, 42, 70,-119, -87,-126, 121,
- 122, 118, 120, -62, 99, -79, 38, -33,
- -38, 41, 109, 62, 98, -32,-106, 18,
- 52, -65, 57, -90, 63,-119, 94, -15,
- 109, 14, -29, 108, 40, -95, 30, 32,
- 29, -53, -62, 3, 63, 65, 7,-124,
- 15, 20, 5, 101, 27, 40, 97, -55,
- -59, -25, 44,-114, 70, 54, 8, -36,
- -13, -88,-115, -2, -66, -14, -21, 113,
- -1, -96, -48, 59, 117, 6,-116, 126,
- -121, 120, 115, 77, -48, -66,-126, -66,
- -37, -62, 70, 65, 43,-116, -6, 48,
- 127, 112, -16, -89, 84,-122, 50,-107,
- -86, 91, 104, 19, 11, -26, -4, -11,
- -54, -66, 125, -97,-119,-118, 65, 27,
- -3, -72, 79, 104, -10, 114, 123, 20,
- -103, -51, -45, 13, -16, 68, 58, -76,
- -90, 102, 83, 51, 11, -53, -95, 16
-};
+#define CMS_RATE 22050
static const byte freqTable[] = {
3, 10, 17, 24, 31, 38, 45, 51,
@@ -797,50 +129,17 @@ static const byte releaseRate[] = {
36, 56, 80, 100, 120, 140, 160, 255
};
-static const uint16 pcjr_freq_table[12] = {
- 65472, 61760, 58304, 55040, 52032, 49024,
- 46272, 43648, 41216, 38912, 36736, 34624
-};
-
static const byte volumeTable[] = {
0x00, 0x10, 0x10, 0x11, 0x11, 0x21, 0x22, 0x22,
0x33, 0x44, 0x55, 0x66, 0x88, 0xAA, 0xCC, 0xFF
};
-static CMSEmulator *g_cmsEmu = 0;
-
-Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
+Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer)
+ : Player_V2Base(scumm, mixer, true) {
int i;
- _isV3Game = (scumm->_game.version >= 3);
- _vm = scumm;
- _mixer = mixer;
-// debug("mixer rate: %d", _mixer->getOutputRate());
- _sampleRate = CMS_RATE;
-
- _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6;
-
- // Initialize sound queue
- _current_nr = _next_nr = 0;
- _current_data = _next_data = 0;
-
- // Initialize channel code
- for (i = 0; i < 4; ++i)
- clear_channel(i);
-
- _next_tick = 0;
- _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ;
-
- // Initialize V3 music timer
- _music_timer_ctr = _music_timer = 0;
- _ticks_per_music_timer = 65535;
-
setMusicVolume(255);
- _timer_output = 0;
- for (i = 0; i < 4; i++)
- _timer_count[i] = 0;
-
memset(_cmsVoicesBase, 0, sizeof(Voice)*16);
memset(_cmsVoices, 0, sizeof(Voice2)*8);
memset(_cmsChips, 0, sizeof(MusicChip)*2);
@@ -876,7 +175,7 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
_cmsVoices[7].octaveOutput = &(_cmsChips[1].octave[1]);
// inits the CMS Emulator like in the original
- g_cmsEmu = new CMSEmulator(_sampleRate);
+ _cmsEmu = new CMSEmulator(_sampleRate);
static const byte cmsInitData[13*2] = {
0x1C, 0x02,
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00,
@@ -886,8 +185,8 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) {
i = 0;
for (int cmsPort = 0x220; i < 2; cmsPort += 2, ++i) {
for (int off = 0; off < 13; ++off) {
- g_cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]);
- g_cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]);
+ _cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]);
+ _cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]);
}
}
@@ -898,40 +197,12 @@ Player_V2CMS::~Player_V2CMS() {
Common::StackLock lock(_mutex);
_mixer->stopHandle(_soundHandle);
- delete g_cmsEmu;
+ delete _cmsEmu;
}
void Player_V2CMS::setMusicVolume(int vol) {
}
-void Player_V2CMS::chainSound(int nr, byte *data) {
- int offset = _header_len + 10;
-
- _current_nr = nr;
- _current_data = data;
-
- for (int i = 0; i < 4; i++) {
- clear_channel(i);
-
- _channels[i].d.music_script_nr = nr;
- if (data) {
- _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
- if (_channels[i].d.next_cmd) {
- _channels[i].d.time_left = 1;
- }
- }
- }
- _music_timer = 0;
-}
-
-void Player_V2CMS::chainNextSound() {
- if (_next_nr) {
- chainSound(_next_nr, _next_data);
- _next_nr = 0;
- _next_data = 0;
- }
-}
-
void Player_V2CMS::stopAllSounds() {
Common::StackLock lock(_mutex);
@@ -1083,275 +354,6 @@ int Player_V2CMS::getSoundStatus(int nr) const {
return _current_nr == nr || _next_nr == nr || _loadedMidiSong == nr;
}
-
-void Player_V2CMS::clear_channel(int i) {
- ChannelInfo *channel = &_channels[i];
- memset(channel, 0, sizeof(ChannelInfo));
-}
-
-int Player_V2CMS::getMusicTimer() {
- if (_isV3Game)
- return _music_timer;
- else
- return _channels[0].d.music_timer;
-}
-
-void Player_V2CMS::execute_cmd(ChannelInfo *channel) {
- uint16 value;
- int16 offset;
- uint8 *script_ptr;
- ChannelInfo * current_channel;
- ChannelInfo * dest_channel;
-
- current_channel = channel;
-
- if (channel->d.next_cmd == 0)
- goto check_stopped;
- script_ptr = &_current_data[channel->d.next_cmd];
-
- for (;;) {
- uint8 opcode = *script_ptr++;
- if (opcode >= 0xf8) {
- switch (opcode) {
- case 0xf8: // set hull curve
- debug(7, "channels[%d]: hull curve %2d",
- (uint)(channel - _channels), *script_ptr);
- channel->d.hull_curve = hull_offsets[*script_ptr / 2];
- script_ptr++;
- break;
-
- case 0xf9: // set freqmod curve
- debug(7, "channels[%d]: freqmod curve %2d",
- (uint)(channel - _channels), *script_ptr);
- channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
- channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
- script_ptr++;
- break;
-
- case 0xfd: // clear other channel
- value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
- debug(7, "clear channel %d", value);
- script_ptr += 2;
- // In Indy3, when traveling to Venice a command is
- // issued to clear channel 4. So we introduce a 4th
- // channel, which is never used. All OOB accesses are
- // mapped to this channel.
- //
- // The original game had room for 8 channels, but only
- // channels 0-3 are read, changes to other channels
- // had no effect.
- if (value >= ARRAYSIZE (_channels))
- value = 4;
- channel = &_channels[value];
- // fall through
-
- case 0xfa: // clear current channel
- if (opcode == 0xfa)
- debug(7, "clear channel");
- channel->d.next_cmd = 0;
- channel->d.base_freq = 0;
- channel->d.freq_delta = 0;
- channel->d.freq = 0;
- channel->d.volume = 0;
- channel->d.volume_delta = 0;
- channel->d.inter_note_pause = 0;
- channel->d.transpose = 0;
- channel->d.hull_curve = 0;
- channel->d.hull_offset = 0;
- channel->d.hull_counter = 0;
- channel->d.freqmod_table = 0;
- channel->d.freqmod_offset = 0;
- channel->d.freqmod_incr = 0;
- channel->d.freqmod_multiplier = 0;
- channel->d.freqmod_modulo = 0;
- break;
-
- case 0xfb: // ret from subroutine
- debug(7, "ret from sub");
- script_ptr = _retaddr;
- break;
-
- case 0xfc: // call subroutine
- offset = READ_LE_UINT16 (script_ptr);
- debug(7, "subroutine %d", offset);
- script_ptr += 2;
- _retaddr = script_ptr;
- script_ptr = _current_data + offset;
- break;
-
- case 0xfe: // loop music
- opcode = *script_ptr++;
- offset = READ_LE_UINT16 (script_ptr);
- script_ptr += 2;
- debug(7, "loop if %d to %d", opcode, offset);
- if (!channel->array[opcode / 2] || --channel->array[opcode/2])
- script_ptr += offset;
- break;
-
- case 0xff: // set parameter
- opcode = *script_ptr++;
- value = READ_LE_UINT16 (script_ptr);
- channel->array[opcode / 2] = value;
- debug(7, "channels[%d]: set param %2d = %5d",
- (uint)(channel - _channels), opcode, value);
- script_ptr += 2;
- if (opcode == 14) {
- /* tempo var */
- _ticks_per_music_timer = 125;
- }
- if (opcode == 0)
- goto end;
- break;
- }
- } else { // opcode < 0xf8
- for (;;) {
- int16 note, octave;
- int is_last_note;
- dest_channel = &_channels[(opcode >> 5) & 3];
-
- if (!(opcode & 0x80)) {
-
- int tempo = channel->d.tempo;
- if (!tempo)
- tempo = 1;
- channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
-
- note = *script_ptr++;
- is_last_note = note & 0x80;
- note &= 0x7f;
- if (note == 0x7f) {
- debug(8, "channels[%d]: pause %d",
- (uint)(channel - _channels), channel->d.time_left);
- goto end;
- }
- } else {
-
- channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
-
- if ((opcode & 0x10)) {
- debug(8, "channels[%d]: pause %d",
- (uint)(channel - _channels), channel->d.time_left);
- goto end;
- }
-
- is_last_note = 0;
- note = (*script_ptr++) & 0x7f;
- }
-
- debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
- (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0,
- note, (signed short) dest_channel->d.transpose, channel->d.time_left,
- dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
- dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
- is_last_note ? "last":"");
-
- uint16 myfreq;
- dest_channel->d.time_left = channel->d.time_left;
- dest_channel->d.note_length =
- channel->d.time_left - dest_channel->d.inter_note_pause;
- note += dest_channel->d.transpose;
- while (note < 0)
- note += 12;
- octave = note / 12;
- note = note % 12;
- dest_channel->d.hull_offset = 0;
- dest_channel->d.hull_counter = 1;
- if (dest_channel == &_channels[3]) {
- dest_channel->d.hull_curve = 196 + note * 12;
- myfreq = 384 - 64 * octave;
- } else {
- myfreq = pcjr_freq_table[note] >> octave;
- }
- dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
- if (is_last_note)
- goto end;
- opcode = *script_ptr++;
- }
- }
- }
-
-end:
- channel = current_channel;
- if (channel->d.time_left) {
- channel->d.next_cmd = script_ptr - _current_data;
- return;
- }
-
- channel->d.next_cmd = 0;
-
-check_stopped:
- int i;
- for (i = 0; i < 4; i++) {
- if (_channels[i].d.time_left)
- return;
- }
-
- _current_nr = 0;
- _current_data = 0;
- chainNextSound();
- return;
-}
-
-void Player_V2CMS::next_freqs(ChannelInfo *channel) {
- channel->d.volume += channel->d.volume_delta;
- channel->d.base_freq += channel->d.freq_delta;
-
- channel->d.freqmod_offset += channel->d.freqmod_incr;
- if (channel->d.freqmod_offset != 0)
- if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
- channel->d.freqmod_offset -= channel->d.freqmod_modulo;
-
- channel->d.freq =
- (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
- * (int) channel->d.freqmod_multiplier / 256
- + channel->d.base_freq;
-
- debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
- channel->d.base_freq, (int16)channel->d.freq_delta,
- channel->d.freqmod_table, channel->d.freqmod_offset,
- channel->d.freqmod_incr, channel->d.freqmod_multiplier,
- channel->d.freq);
-
- if (channel->d.note_length && !--channel->d.note_length) {
- channel->d.hull_offset = 16;
- channel->d.hull_counter = 1;
- }
-
- if (!--channel->d.time_left) {
- execute_cmd(channel);
- }
-
- if (channel->d.hull_counter && !--channel->d.hull_counter) {
- for (;;) {
- const int16 *hull_ptr = hulls
- + channel->d.hull_curve + channel->d.hull_offset / 2;
- if (hull_ptr[1] == -1) {
- channel->d.volume = hull_ptr[0];
- if (hull_ptr[0] == 0)
- channel->d.volume_delta = 0;
- channel->d.hull_offset += 4;
- } else {
- channel->d.volume_delta = hull_ptr[0];
- channel->d.hull_counter = hull_ptr[1];
- channel->d.hull_offset += 4;
- break;
- }
- }
- }
-}
-
-void Player_V2CMS::nextTick() {
- for (int i = 0; i < 4; i++) {
- if (!_channels[i].d.time_left)
- continue;
- next_freqs(&_channels[i]);
- }
- if (_music_timer_ctr++ >= _ticks_per_music_timer) {
- _music_timer_ctr = 0;
- _music_timer++;
- }
-}
-
void Player_V2CMS::processMidiData(uint ticks) {
byte *currentData = _midiData;
byte command = 0x00;
@@ -1412,7 +414,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) {
Common::StackLock lock(_mutex);
uint step = 1;
- int len = numSamples/2;
+ int len = numSamples / 2;
// maybe this needs a complete rewrite
do {
@@ -1442,7 +444,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) {
step = len;
if (step > (_next_tick >> FIXP_SHIFT))
step = (_next_tick >> FIXP_SHIFT);
- g_cmsEmu->readBuffer(buffer, step);
+ _cmsEmu->readBuffer(buffer, step);
buffer += 2 * step;
_next_tick -= step << FIXP_SHIFT;
} while (len -= step);
@@ -1486,27 +488,27 @@ void Player_V2CMS::playVoice() {
void Player_V2CMS::processChannel(Voice2 *channel) {
++_outputTableReady;
switch (channel->nextProcessState) {
- case PROCESS_RELEASE:
- processRelease(channel);
+ case PROCESS_RELEASE:
+ processRelease(channel);
break;
- case PROCESS_ATTACK:
- processAttack(channel);
+ case PROCESS_ATTACK:
+ processAttack(channel);
break;
- case PROCESS_DECAY:
- processDecay(channel);
+ case PROCESS_DECAY:
+ processDecay(channel);
break;
- case PROCESS_SUSTAIN:
- processSustain(channel);
+ case PROCESS_SUSTAIN:
+ processSustain(channel);
break;
- case PROCESS_VIBRATO:
- processVibrato(channel);
+ case PROCESS_VIBRATO:
+ processVibrato(channel);
break;
- default:
+ default:
break;
}
}
@@ -1588,8 +590,8 @@ void Player_V2CMS::offAllChannels() {
for (int cmsPort = 0x220, i = 0; i < 2; cmsPort += 2, ++i) {
for (int off = 0; off < 10; ++off) {
- g_cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]);
- g_cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]);
+ _cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]);
+ _cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]);
}
}*/
}
@@ -1778,32 +780,32 @@ void Player_V2CMS::play() {
// with the high nibble of the volumeReg value
// the right channels amplitude is set
// with the low value the left channels amplitude
- g_cmsEmu->portWrite(0x221, 0);
- g_cmsEmu->portWrite(0x220, volumeReg[0]);
- g_cmsEmu->portWrite(0x221, 1);
- g_cmsEmu->portWrite(0x220, volumeReg[1]);
- g_cmsEmu->portWrite(0x221, 2);
- g_cmsEmu->portWrite(0x220, volumeReg[2]);
- g_cmsEmu->portWrite(0x221, 3);
- g_cmsEmu->portWrite(0x220, volumeReg[3]);
- g_cmsEmu->portWrite(0x221, 8);
- g_cmsEmu->portWrite(0x220, freqReg[0]);
- g_cmsEmu->portWrite(0x221, 9);
- g_cmsEmu->portWrite(0x220, freqReg[1]);
- g_cmsEmu->portWrite(0x221, 10);
- g_cmsEmu->portWrite(0x220, freqReg[2]);
- g_cmsEmu->portWrite(0x221, 11);
- g_cmsEmu->portWrite(0x220, freqReg[3]);
- g_cmsEmu->portWrite(0x221, 0x10);
- g_cmsEmu->portWrite(0x220, octaveReg[0]);
- g_cmsEmu->portWrite(0x221, 0x11);
- g_cmsEmu->portWrite(0x220, octaveReg[1]);
- g_cmsEmu->portWrite(0x221, 0x14);
- g_cmsEmu->portWrite(0x220, freqEnable);
- g_cmsEmu->portWrite(0x221, 0x15);
- g_cmsEmu->portWrite(0x220, noiseEnable);
- g_cmsEmu->portWrite(0x221, 0x16);
- g_cmsEmu->portWrite(0x220, noiseGen);
+ _cmsEmu->portWrite(0x221, 0);
+ _cmsEmu->portWrite(0x220, volumeReg[0]);
+ _cmsEmu->portWrite(0x221, 1);
+ _cmsEmu->portWrite(0x220, volumeReg[1]);
+ _cmsEmu->portWrite(0x221, 2);
+ _cmsEmu->portWrite(0x220, volumeReg[2]);
+ _cmsEmu->portWrite(0x221, 3);
+ _cmsEmu->portWrite(0x220, volumeReg[3]);
+ _cmsEmu->portWrite(0x221, 8);
+ _cmsEmu->portWrite(0x220, freqReg[0]);
+ _cmsEmu->portWrite(0x221, 9);
+ _cmsEmu->portWrite(0x220, freqReg[1]);
+ _cmsEmu->portWrite(0x221, 10);
+ _cmsEmu->portWrite(0x220, freqReg[2]);
+ _cmsEmu->portWrite(0x221, 11);
+ _cmsEmu->portWrite(0x220, freqReg[3]);
+ _cmsEmu->portWrite(0x221, 0x10);
+ _cmsEmu->portWrite(0x220, octaveReg[0]);
+ _cmsEmu->portWrite(0x221, 0x11);
+ _cmsEmu->portWrite(0x220, octaveReg[1]);
+ _cmsEmu->portWrite(0x221, 0x14);
+ _cmsEmu->portWrite(0x220, freqEnable);
+ _cmsEmu->portWrite(0x221, 0x15);
+ _cmsEmu->portWrite(0x220, noiseEnable);
+ _cmsEmu->portWrite(0x221, 0x16);
+ _cmsEmu->portWrite(0x220, noiseGen);
}
void Player_V2CMS::playMusicChips(const MusicChip *table) {
@@ -1811,30 +813,30 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) {
do {
cmsPort += 2;
- g_cmsEmu->portWrite(cmsPort+1, 0);
- g_cmsEmu->portWrite(cmsPort, table->ampl[0]);
- g_cmsEmu->portWrite(cmsPort+1, 1);
- g_cmsEmu->portWrite(cmsPort, table->ampl[1]);
- g_cmsEmu->portWrite(cmsPort+1, 2);
- g_cmsEmu->portWrite(cmsPort, table->ampl[2]);
- g_cmsEmu->portWrite(cmsPort+1, 3);
- g_cmsEmu->portWrite(cmsPort, table->ampl[3]);
- g_cmsEmu->portWrite(cmsPort+1, 8);
- g_cmsEmu->portWrite(cmsPort, table->freq[0]);
- g_cmsEmu->portWrite(cmsPort+1, 9);
- g_cmsEmu->portWrite(cmsPort, table->freq[1]);
- g_cmsEmu->portWrite(cmsPort+1, 10);
- g_cmsEmu->portWrite(cmsPort, table->freq[2]);
- g_cmsEmu->portWrite(cmsPort+1, 11);
- g_cmsEmu->portWrite(cmsPort, table->freq[3]);
- g_cmsEmu->portWrite(cmsPort+1, 0x10);
- g_cmsEmu->portWrite(cmsPort, table->octave[0]);
- g_cmsEmu->portWrite(cmsPort+1, 0x11);
- g_cmsEmu->portWrite(cmsPort, table->octave[1]);
- g_cmsEmu->portWrite(cmsPort+1, 0x14);
- g_cmsEmu->portWrite(cmsPort, 0x3F);
- g_cmsEmu->portWrite(cmsPort+1, 0x15);
- g_cmsEmu->portWrite(cmsPort, 0x00);
+ _cmsEmu->portWrite(cmsPort+1, 0);
+ _cmsEmu->portWrite(cmsPort, table->ampl[0]);
+ _cmsEmu->portWrite(cmsPort+1, 1);
+ _cmsEmu->portWrite(cmsPort, table->ampl[1]);
+ _cmsEmu->portWrite(cmsPort+1, 2);
+ _cmsEmu->portWrite(cmsPort, table->ampl[2]);
+ _cmsEmu->portWrite(cmsPort+1, 3);
+ _cmsEmu->portWrite(cmsPort, table->ampl[3]);
+ _cmsEmu->portWrite(cmsPort+1, 8);
+ _cmsEmu->portWrite(cmsPort, table->freq[0]);
+ _cmsEmu->portWrite(cmsPort+1, 9);
+ _cmsEmu->portWrite(cmsPort, table->freq[1]);
+ _cmsEmu->portWrite(cmsPort+1, 10);
+ _cmsEmu->portWrite(cmsPort, table->freq[2]);
+ _cmsEmu->portWrite(cmsPort+1, 11);
+ _cmsEmu->portWrite(cmsPort, table->freq[3]);
+ _cmsEmu->portWrite(cmsPort+1, 0x10);
+ _cmsEmu->portWrite(cmsPort, table->octave[0]);
+ _cmsEmu->portWrite(cmsPort+1, 0x11);
+ _cmsEmu->portWrite(cmsPort, table->octave[1]);
+ _cmsEmu->portWrite(cmsPort+1, 0x14);
+ _cmsEmu->portWrite(cmsPort, 0x3F);
+ _cmsEmu->portWrite(cmsPort+1, 0x15);
+ _cmsEmu->portWrite(cmsPort, 0x00);
++table;
} while ((cmsPort & 2) == 0);
}
diff --git a/engines/scumm/player_v2cms.h b/engines/scumm/player_v2cms.h
new file mode 100644
index 0000000000..fd939d8505
--- /dev/null
+++ b/engines/scumm/player_v2cms.h
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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_V2CMS_H
+#define SCUMM_PLAYER_V2CMS_H
+
+#include "scumm/player_v2base.h" // for channel_data
+
+class CMSEmulator;
+
+namespace Scumm {
+
+/**
+ * Scumm V2 CMS/Gameblaster MIDI driver.
+ */
+class Player_V2CMS : public Player_V2Base {
+public:
+ Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer);
+ virtual ~Player_V2CMS();
+
+ // MusicEngine API
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+// virtual int getMusicTimer();
+ virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+protected:
+
+#include "common/pack-start.h" // START STRUCT PACKING
+ struct Voice {
+ byte attack;
+ byte decay;
+ byte sustain;
+ byte release;
+ byte octadd;
+ int16 vibrato;
+ int16 vibrato2;
+ int16 noise;
+ } PACKED_STRUCT;
+
+ struct Voice2 {
+ byte *amplitudeOutput;
+ byte *freqOutput;
+ byte *octaveOutput;
+
+ uint8 channel;
+ int8 sustainLevel;
+ int8 attackRate;
+ uint8 maxAmpl;
+ int8 decayRate;
+ int8 sustainRate;
+ int8 releaseRate;
+ int8 releaseTime;
+ int8 vibratoRate;
+ int8 vibratoDepth;
+
+ int8 curVibratoRate;
+ int8 curVibratoUnk;
+
+ int8 unkVibratoRate;
+ int8 unkVibratoDepth;
+
+ int8 unkRate;
+ int8 unkCount;
+
+ int nextProcessState;
+ int8 curVolume;
+ int8 curOctave;
+ int8 curFreq;
+
+ int8 octaveAdd;
+
+ int8 playingNote;
+ Voice2 *nextVoice;
+
+ byte chanNumber;
+ } PACKED_STRUCT;
+
+ struct MusicChip {
+ byte ampl[4];
+ byte freq[4];
+ byte octave[2];
+ } PACKED_STRUCT;
+#include "common/pack-end.h" // END STRUCT PACKING
+
+ Voice _cmsVoicesBase[16];
+ Voice2 _cmsVoices[8];
+ MusicChip _cmsChips[2];
+
+ int8 _tempo;
+ int8 _tempoSum;
+ byte _looping;
+ byte _octaveMask;
+ int16 _midiDelay;
+ Voice2 *_midiChannel[16];
+ byte _midiChannelUse[16];
+ byte *_midiData;
+ byte *_midiSongBegin;
+
+ int _loadedMidiSong;
+
+ byte _lastMidiCommand;
+ uint _outputTableReady;
+ byte _clkFrequenz;
+ byte _restart;
+ byte _curSno;
+
+ void loadMidiData(byte *data, int sound);
+ void play();
+
+ void processChannel(Voice2 *channel);
+ void processRelease(Voice2 *channel);
+ void processAttack(Voice2 *channel);
+ void processDecay(Voice2 *channel);
+ void processSustain(Voice2 *channel);
+ void processVibrato(Voice2 *channel);
+
+ void playMusicChips(const MusicChip *table);
+ void playNote(byte *&data);
+ void clearNote(byte *&data);
+ void offAllChannels();
+ void playVoice();
+ void processMidiData(uint ticks);
+
+ Voice2 *getFreeVoice();
+ Voice2 *getPlayVoice(byte param);
+
+ // from Player_V2
+protected:
+ CMSEmulator *_cmsEmu;
+
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index d9a5e3414c..5aae59d987 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -1012,7 +1012,7 @@ void ResourceManager::freeResources() {
void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source) {
byte *alloced;
- int i, len;
+ int len;
_res->nukeResource(type, resindex);
@@ -1024,12 +1024,13 @@ void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source)
alloced = _res->createResource(type, resindex, len);
if (!source) {
- alloced[0] = fetchScriptByte();
- for (i = 1; i < len; i++)
- alloced[i] = *_scriptPointer++;
+ // Need to refresh the script pointer, since createResource may
+ // have caused the script resource to expire.
+ refreshScriptPointer();
+ memcpy(alloced, _scriptPointer, len);
+ _scriptPointer += len;
} else {
- for (i = 0; i < len; i++)
- alloced[i] = source[i];
+ memcpy(alloced, source, len);
}
}
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 ca48a2b86a..9cb603e27f 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -378,10 +378,10 @@ bool ScummEngine::loadState(int slot, bool compat) {
return false;
}
- _engineStartTime = _system->getMillis() / 1000 - infos.playtime;
+ setTotalPlayTime(infos.playtime * 1000);
} else {
// start time counting
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime();
}
// Due to a bug in scummvm up to and including 0.3.0, save games could be saved
@@ -797,7 +797,7 @@ void ScummEngine::saveInfos(Common::WriteStream* file) {
// still save old format for older versions
section.timeTValue = 0;
- section.playtime = _system->getMillis() / 1000 - _engineStartTime;
+ section.playtime = getTotalPlayTime() / 1000;
TimeDate curTime;
_system->getTimeAndDate(curTime);
@@ -1294,9 +1294,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
@@ -1459,6 +1488,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 44c8cc7d60..91e780bcd1 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 81
+#define CURRENT_VER 84
/**
* 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 223e9822e2..b6058d4d9a 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -339,7 +339,7 @@ void ScummEngine::runScriptNested(int script) {
_currentScript = script;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
executeScript();
if (vm.numNestedScripts != 0)
@@ -354,7 +354,7 @@ void ScummEngine::runScriptNested(int script) {
slot->status != ssDead && slot->freezeCount == 0) {
_currentScript = nest->slot;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
return;
}
}
@@ -440,13 +440,27 @@ void ScummEngine::getScriptBaseAddress() {
}
}
-
-void ScummEngine::getScriptEntryPoint() {
+void ScummEngine::resetScriptPointer() {
if (_currentScript == 0xFF)
return;
_scriptPointer = _scriptOrgPointer + vm.slot[_currentScript].offs;
}
+/**
+ * This method checks whether the resource that contains the active script
+ * moved, and if so, updates the script pointer accordingly.
+ *
+ * The script resource may have moved because it might have been garbage
+ * collected by ResourceManager::expireResources.
+ */
+void ScummEngine::refreshScriptPointer() {
+ if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
+ long oldoffs = _scriptPointer - _scriptOrgPointer;
+ getScriptBaseAddress();
+ _scriptPointer = _scriptOrgPointer + oldoffs;
+ }
+}
+
/** Execute a script - Read opcode, and execute it from the table */
void ScummEngine::executeScript() {
int c;
@@ -492,20 +506,12 @@ const char *ScummEngine::getOpcodeDesc(byte i) {
}
byte ScummEngine::fetchScriptByte() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
return *_scriptPointer++;
}
uint ScummEngine::fetchScriptWord() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
uint a = READ_LE_UINT16(_scriptPointer);
_scriptPointer += 2;
return a;
@@ -516,11 +522,7 @@ int ScummEngine::fetchScriptWordSigned() {
}
uint ScummEngine::fetchScriptDWord() {
- if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
- long oldoffs = _scriptPointer - _scriptOrgPointer;
- getScriptBaseAddress();
- _scriptPointer = _scriptOrgPointer + oldoffs;
- }
+ refreshScriptPointer();
uint a = READ_LE_UINT32(_scriptPointer);
_scriptPointer += 4;
return a;
@@ -898,7 +900,7 @@ void ScummEngine::runAllScripts() {
if (vm.slot[i].cycle == cycle && vm.slot[i].status == ssRunning && !vm.slot[i].didexec) {
_currentScript = (byte)i;
getScriptBaseAddress();
- getScriptEntryPoint();
+ resetScriptPointer();
executeScript();
}
}
@@ -1231,22 +1233,26 @@ bool ScummEngine::isRoomScriptRunning(int script) const {
void ScummEngine::copyScriptString(byte *dst) {
int len = resStrLen(_scriptPointer) + 1;
- while (len--)
- *dst++ = fetchScriptByte();
+ memcpy(dst, _scriptPointer, len);
+ _scriptPointer += len;
+ dst += len;
*dst = 0;
}
-//
-// Given a pointer to a Scumm string, this function returns the total byte length
-// of the string data in that resource. To do so it has to understand certain
-// special characters embedded into the string. The reason for this function is that
-// sometimes this embedded data contains zero bytes, thus we can't just use strlen.
-//
-int ScummEngine::resStrLen(const byte *src) const {
+/**
+ * Given a pointer to a Scumm string, this function returns the total
+ * byte length of the string data in that resource. To do so it has to
+ * understand certain special characters embedded into the string. The
+ * reason for this function is that sometimes this embedded data
+ * contains zero bytes, thus we can't just use strlen.
+ */
+int ScummEngine::resStrLen(const byte *src) {
int num = 0;
byte chr;
- if (src == NULL)
+ if (src == NULL) {
+ refreshScriptPointer();
src = _scriptPointer;
+ }
while ((chr = *src++) != 0) {
num++;
if (_game.heversion <= 71 && chr == 0xFF) {
diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp
index cf44ee195e..d3f256c951 100644
--- a/engines/scumm/script_v0.cpp
+++ b/engines/scumm/script_v0.cpp
@@ -829,6 +829,8 @@ void ScummEngine_v0::o_setActorBitVar() {
// This flag causes the actor to stop moving (used by script #158, Green Tentacle 'Oomph!')
if (a->_miscflags & 0x40)
a->stopActorMoving();
+ if (a->_miscflags & 0x80)
+ a->setActorCostume(0);
debug(0, "o_setActorBitVar(%d, %d, %d)", act, mask, mod);
}
@@ -987,7 +989,10 @@ void ScummEngine_v0::o_setOwnerOf() {
void ScummEngine_v0::resetSentence(bool walking) {
_activeVerb = 13;
- if (!walking) {
+ // If the actor is walking, or the screen is a keypad (no sentence verbs/objects are drawn)
+ // Then reset all active objects (stops the radio crash, bug #3077966)
+ if (!walking || !(_userState & 32)) {
+ _v0ObjectFlag = 0;
_activeInventory = 0;
_activeObject = 0;
_activeObject2 = 0;
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index bc8446d16f..d3a2272a39 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -1174,6 +1174,8 @@ void ScummEngine_v2::o2_walkActorToObject() {
int obj;
Actor *a;
+ _v0ObjectFlag = 0;
+
a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject");
obj = getVarOrDirectWord(PARAM_2);
if (whereIsObject(obj) != WIO_NOT_FOUND) {
@@ -1182,6 +1184,7 @@ void ScummEngine_v2::o2_walkActorToObject() {
AdjustBoxResult r = a->adjustXYToBeInBox(x, y);
x = r.x;
y = r.y;
+
a->startWalkActor(x, y, dir);
}
}
diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index 6dc3004432..b6e5834acc 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -111,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 8d986afc66..d3e1ba43ef 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -1617,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);
@@ -1712,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 {
@@ -1808,17 +1840,12 @@ void ScummEngine_v5::o5_roomOps() {
Common::InSaveFile *file = _saveFileMan->openForLoading(filename);
if (file != NULL) {
byte *ptr;
- int len = 256, cnt = 0;
- ptr = (byte *)malloc(len);
- while (ptr) {
- int r = file->read(ptr + cnt, len - cnt);
- cnt += r;
- if (cnt < len)
- break;
- len *= 2;
- ptr = (byte *)realloc(ptr, len);
- }
- ptr[cnt] = '\0';
+ const int len = file->size();
+ ptr = (byte *)malloc(len + 1);
+ assert(ptr);
+ int r = file->read(ptr, len);
+ assert(r == len);
+ ptr[len] = '\0';
loadPtrToResource(rtString, a, ptr);
free(ptr);
delete file;
@@ -2124,6 +2151,7 @@ void ScummEngine_v5::o5_stringOps() {
case 2: /* copystring */
a = getVarOrDirectByte(PARAM_1);
b = getVarOrDirectByte(PARAM_2);
+ assert(a != b);
_res->nukeResource(rtString, a);
ptr = getResourceAddress(rtString, b);
if (ptr)
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index c5841dfaf4..0226343df5 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -536,7 +536,18 @@ void ScummEngine_v6::o6_not() {
}
void ScummEngine_v6::o6_eq() {
- push(pop() == pop());
+ int a = pop();
+ int b = pop();
+
+ // WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions,
+ // when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions.
+ // The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
+ if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
+ a == 2 && b == 1) {
+ push(1);
+ } else {
+ push(a == b);
+ }
}
void ScummEngine_v6::o6_neq() {
@@ -1285,7 +1296,7 @@ void ScummEngine_v6::o6_loadRoomWithEgo() {
void ScummEngine_v6::o6_getRandomNumber() {
int rnd;
- rnd = _rnd.getRandomNumber(pop());
+ rnd = _rnd.getRandomNumber(ABS(pop()));
if (VAR_RANDOM_NR != 0xFF)
VAR(VAR_RANDOM_NR) = rnd;
push(rnd);
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index a25fac1a88..40eeba3663 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 Mon Oct 18 00:42:16 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 },
@@ -132,7 +132,7 @@ static const MD5Table md5table[] = {
{ "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS },
{ "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
- { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Patched", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2e8a1f76ea33bc5e04347646feee173d", "pajama3", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", "CD", "CD", 8955, Common::DE_DEU, Common::kPlatformPC },
@@ -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 },
@@ -609,7 +609,7 @@ static const MD5Table md5table[] = {
{ "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown },
{ "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformPC },
{ "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows },
- { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "Demo", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh },
{ "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", "", "", 79382, Common::EN_USA, Common::kPlatformUnknown },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 75c507565c..42be33fff3 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"
@@ -60,6 +61,7 @@
#include "scumm/player_pce.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
+#include "scumm/player_v2cms.h"
#include "scumm/player_v2a.h"
#include "scumm/player_v3a.h"
#include "scumm/player_v4a.h"
@@ -135,7 +137,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
uint tmpVal;
tmpStr[0] = dr.md5[2*i];
tmpStr[1] = dr.md5[2*i+1];
- sscanf(tmpStr, "%x", &tmpVal);
+ int res = sscanf(tmpStr, "%x", &tmpVal);
+ assert(res == 1);
_gameMD5[i] = (byte)tmpVal;
}
@@ -144,6 +147,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
// Init all vars
_v0ObjectIndex = false;
_v0ObjectInInventory = false;
+ _v0ObjectFlag = 0;
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
@@ -208,7 +212,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_saveLoadSlot = 0;
_lastSaveTime = 0;
_saveTemporaryState = false;
- memset(_saveLoadFileName, 0, sizeof(_saveLoadFileName));
memset(_saveLoadName, 0, sizeof(_saveLoadName));
memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));
_scriptPointer = NULL;
@@ -257,7 +260,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_switchRoomEffect2 = 0;
_switchRoomEffect = 0;
- _bytesPerPixel = 1;
+ _bytesPerPixelOutput = _bytesPerPixel = 1;
_doEffect = false;
_snapScroll = false;
_currentLights = 0;
@@ -277,6 +280,11 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_hePalettes = NULL;
_hePaletteSlot = 0;
_16BitPalette = NULL;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _townsScreen = 0;
+#endif
+ _cjkFont = 0;
+ _cjkChar = 0;
_shadowPalette = NULL;
_shadowPaletteSize = 0;
memset(_currentPalette, 0, sizeof(_currentPalette));
@@ -317,6 +325,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
//
@@ -532,16 +549,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);
@@ -607,7 +629,13 @@ ScummEngine::~ScummEngine() {
free(_compositeBuf);
free(_herculesBuf);
- free(_fmtownsBuf);
+
+ free(_16BitPalette);
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ delete _townsScreen;
+#endif
+ delete _cjkFont;
delete _debugger;
@@ -697,6 +725,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)
@@ -1111,16 +1143,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));
}
}
@@ -1236,13 +1281,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
@@ -1320,6 +1360,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)) {
@@ -1512,8 +1570,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);
@@ -1591,6 +1647,10 @@ void ScummEngine_v90he::resetScumm() {
_logicHE = new LogicHEsoccer(this);
break;
+ case GID_BASEBALL2001:
+ _logicHE = new LogicHEbaseball2001(this);
+ break;
+
case GID_BASKETBALL:
_logicHE = new LogicHEbasketball(this);
break;
@@ -1760,9 +1820,9 @@ void ScummEngine::setupMusic(int midi) {
} 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(this, _mixer);
+ _musicEngine = _townsPlayer = new Player_Towns_v1(this, _mixer);
if (!_townsPlayer->init())
- error("Failed to initialize FM-Towns audio driver.");
+ error("Failed to initialize FM-Towns audio driver");
} else if (_game.version >= 3 && _game.heversion <= 62) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
@@ -1777,7 +1837,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,
@@ -1786,17 +1855,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);*/
}
}
}
@@ -1858,7 +1927,7 @@ int ScummEngine::getTalkSpeed() {
#pragma mark -
Common::Error ScummEngine::go() {
- _engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime();
// If requested, load a save game instead of running the boot script
if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveTemporaryState)) {
@@ -1923,6 +1992,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;
@@ -1930,6 +2005,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;
@@ -2044,6 +2125,10 @@ load_game:
goto load_game;
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ towns_processPalCycleField();
+#endif
+
if (_currentRoom == 0) {
if (_game.version > 3)
CHARSET_1();
@@ -2113,6 +2198,7 @@ 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.
@@ -2122,6 +2208,7 @@ void ScummEngine::scummLoop_updateScummVars() {
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;
@@ -2167,14 +2254,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;
@@ -2186,7 +2273,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);
@@ -2418,10 +2505,6 @@ void ScummEngine::startManiac() {
void ScummEngine::pauseEngineIntern(bool pause) {
if (pause) {
- // Record start of the pause, so that we can later
- // adjust _engineStartTime accordingly.
- _pauseStartTime = _system->getMillis();
-
// Pause sound & video
_oldSoundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
@@ -2429,14 +2512,16 @@ 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
_sound->pauseSounds(_oldSoundsPaused);
-
- // Adjust engine start time
- _engineStartTime += (_system->getMillis() - _pauseStartTime) / 1000;
- _pauseStartTime = 0;
}
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 1357bad8cf..0a513b6068 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -36,6 +36,7 @@
#include "common/rect.h"
#include "common/str.h"
#include "graphics/surface.h"
+#include "graphics/sjis.h"
#include "scumm/gfx.h"
#include "scumm/detection.h"
@@ -43,6 +44,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;
}
@@ -55,10 +67,14 @@ namespace Common {
/**
* This is the namespace of the SCUMM engine.
*
- * Status of this engine: ???
+ * Status of this engine:
+ * Complete support for all SCUMM based LucasArts adventures.
+ * Complete support for many Humongous Entertainment games,
+ * but for some of the newer ones, this is still work in progress.
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Classic 2D LucasArts adventures
+ * - numerous Humongous Entertainment games
*/
namespace Scumm {
@@ -237,6 +253,7 @@ enum ScummGameId {
GID_FUNSHOP, // Used for all three funshops
GID_FOOTBALL,
GID_SOCCER,
+ GID_BASEBALL2001,
GID_BASKETBALL,
GID_MOONBASE,
GID_HECUP // CUP demos
@@ -576,6 +593,7 @@ protected:
bool _v0ObjectIndex; // V0 Use object index, instead of object number
bool _v0ObjectInInventory; // V0 Use object number from inventory
+ byte _v0ObjectFlag;
/* Global resource tables */
int _numVariables, _numBitVariables, _numLocalObjects;
@@ -643,7 +661,7 @@ protected:
byte _saveLoadFlag, _saveLoadSlot;
uint32 _lastSaveTime;
bool _saveTemporaryState;
- char _saveLoadFileName[32];
+ Common::String _saveLoadFileName;
char _saveLoadName[32];
bool saveState(Common::OutSaveFile *out, bool writeHeader = true);
@@ -682,9 +700,6 @@ protected:
void saveInfos(Common::WriteStream* file);
static bool loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff);
- int32 _engineStartTime;
- int32 _pauseStartTime;
-
protected:
/* Script VM - should be in Script class */
uint32 _localScriptOffsets[1024];
@@ -737,9 +752,10 @@ protected:
void stopObjectScript(int script);
void getScriptBaseAddress();
- void getScriptEntryPoint();
+ void resetScriptPointer();
int getVerbEntrypoint(int obj, int entry);
+ void refreshScriptPointer();
byte fetchScriptByte();
virtual uint fetchScriptWord();
virtual int fetchScriptWordSigned();
@@ -759,7 +775,7 @@ protected:
void endOverride();
void copyScriptString(byte *dst);
- int resStrLen(const byte *src) const;
+ int resStrLen(const byte *src);
void doSentence(int c, int b, int a);
/* Should be in Resource class */
@@ -972,6 +988,7 @@ public:
Common::RenderMode _renderMode;
uint8 _bytesPerPixel;
+ uint8 _bytesPerPixelOutput;
protected:
ColorCycle _colorCycle[16]; // Palette cycles
@@ -1044,6 +1061,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);
@@ -1077,7 +1095,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);
@@ -1221,7 +1239,7 @@ protected:
void restoreCharsetBg();
void clearCharsetMask();
void clearTextSurface();
-
+
virtual void initCharset(int charset);
virtual void printString(int m, const byte *msg);
@@ -1397,6 +1415,40 @@ 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];
+ Graphics::FontSJIS *_cjkFont;
+ uint16 _cjkChar;
+
+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/smush/codec47.cpp b/engines/scumm/smush/codec47.cpp
index 62bc0bb098..333fdabccf 100644
--- a/engines/scumm/smush/codec47.cpp
+++ b/engines/scumm/smush/codec47.cpp
@@ -301,9 +301,11 @@ void Codec47Decoder::makeTables47(int width) {
int32 a, c, d;
int16 tmp;
- for (int l = 0; l < 512; l += 2) {
+ for (int l = 0; l < ARRAYSIZE(codec47_table); l += 2) {
_table[l / 2] = (int16)(codec47_table[l + 1] * width + codec47_table[l]);
}
+ // Note: _table[255] is never inited; but since only the first 0xF8
+ // entries of it are used anyway, this doesn't matter.
a = 0;
c = 0;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 708faa5687..8fc6de8af9 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -78,6 +78,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
_curSoundPos(0),
_currentCDSound(0),
_currentMusic(0),
+ _lastSound(0),
_soundsPaused(false),
_sfxMode(0) {
@@ -95,6 +96,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)
@@ -243,7 +245,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.
@@ -312,7 +314,7 @@ 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 && READ_BE_UINT32(ptr) == MKID_BE('SOUN')) {
if (_vm->_game.version != 3)
@@ -789,6 +791,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..4b09547c8c 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();
}
}
@@ -591,7 +601,12 @@ void ScummEngine::CHARSET_1() {
} else if (!(_game.platform == Common::kPlatformFMTowns) && _string[0].height) {
_nextTop += _string[0].height;
} else {
+ bool useCJK = _useCJKMode;
+ // SCUMM5 FM-Towns doesn't use the height of the ROM font here.
+ if (_game.platform == Common::kPlatformFMTowns && _game.version == 5)
+ _useCJKMode = false;
_nextTop += _charset->getFontHeight();
+ _useCJKMode = useCJK;
}
if (_game.version > 3) {
// FIXME: is this really needed?
@@ -624,9 +639,7 @@ void ScummEngine::CHARSET_1() {
#endif
} else {
if (c & 0x80 && _useCJKMode) {
- if (_language == Common::JA_JPN && !checkSJISCode(c)) {
- c = 0x20; //not in S-JIS
- } else {
+ if (checkSJISCode(c)) {
byte *buffer = _charsetBuffer + _charsetBufPos;
c += *buffer++ * 256; //LE
_charsetBufPos = buffer - _charsetBuffer;
@@ -660,6 +673,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());
@@ -978,11 +996,8 @@ void ScummEngine::drawString(int a, const byte *msg) {
}
}
if (c & 0x80 && _useCJKMode) {
- if (_language == Common::JA_JPN && !checkSJISCode(c)) {
- c = 0x20; //not in S-JIS
- } else {
+ if (checkSJISCode(c))
c += buf[i++] * 256;
- }
}
_charset->printChar(c, true);
_charset->_blitAlso = false;
@@ -1006,6 +1021,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
uint num = 0;
uint32 val;
byte chr;
+ byte lastChr = 0;
const byte *src;
byte *end;
byte transBuf[384];
@@ -1113,11 +1129,12 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
} else if (_game.id == GID_DIG && (chr == 1 || chr == 2 || chr == 3 || chr == 8)) {
// Skip these characters
} else {
- if (!(chr == '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
- (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN))
- {
+ if ((chr != '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) ||
+ (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) ||
+ (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN && checkSJISCode(lastChr))) {
*dst++ = chr;
}
+ lastChr = chr;
}
// Check for a buffer overflow
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 5f8a6d9f52..c443f98bc6 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -167,11 +167,6 @@ void ScummEngine_v0::switchActor(int slot) {
if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2)
return;
- // verbs disabled for the current actor
- ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "switchActor");
- if (a->_miscflags & 0x40)
- return;
-
VAR(VAR_EGO) = VAR(97 + slot);
resetVerbs();
actorFollowCamera(VAR(VAR_EGO));
@@ -323,7 +318,7 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
}
}
- if (new_box != _mouseOverBoxV2) {
+ if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) {
if (_mouseOverBoxV2 != -1) {
rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;
@@ -524,9 +519,8 @@ void ScummEngine_v2::handleMouseOver(bool updateInventory) {
}
void ScummEngine_v0::handleMouseOver(bool updateInventory) {
- ScummEngine_v2::handleMouseOver(updateInventory);
-
drawSentence();
+ ScummEngine_v2::handleMouseOver(updateInventory);
}
#ifdef ENABLE_HE
@@ -727,7 +721,7 @@ void ScummEngine_v2::checkExecVerbs() {
}
void ScummEngine_v0::runObject(int obj, int entry) {
- int prev = _v0ObjectInInventory;
+ bool prev = _v0ObjectInInventory;
if (getVerbEntrypoint(obj, entry) == 0) {
// If nothing was found, attempt to find the 'WHAT-IS' verb script
@@ -745,30 +739,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");
@@ -955,7 +936,7 @@ bool ScummEngine_v0::verbExec() {
return true;
}
_v0ObjectInInventory = true;
- VAR(5) = _activeActor;
+ VAR(VAR_ACTIVE_ACTOR) = _activeActor;
runObject(_activeInventory , 3);
_v0ObjectInInventory = false;
@@ -991,6 +972,7 @@ bool ScummEngine_v0::verbExec() {
// We acted on an inventory item
if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) {
_v0ObjectInInventory = true;
+ _activeObject = _activeInventory;
runObject(_activeInventory, _activeVerb);
_verbExecuting = false;
@@ -1049,7 +1031,7 @@ bool ScummEngine_v0::verbExec() {
}
void ScummEngine_v0::checkExecVerbs() {
- Actor *a = derefActor(VAR(VAR_EGO), "checkExecVerbs");
+ ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs");
VirtScreen *zone = findVirtScreen(_mouse.y);
// Is a verb currently executing
@@ -1164,27 +1146,28 @@ void ScummEngine_v0::checkExecVerbs() {
obj = 0;
objIdx = 0;
}
+
+ if (a->_miscflags & 0x80) {
+ if (_activeVerb != 7 && over != 7) {
+ _activeVerb = 0;
+ over = 0;
+ }
+ }
// Handle New Kid verb options
if (_activeVerb == 7 || over == 7) {
// Disable New-Kid (in the secret lab)
if (_currentMode == 2 || _currentMode == 0)
return;
-
- if (!(((ActorC64 *)a)->_miscflags & 0x80)) {
- if (_activeVerb != 7) {
- _activeVerb = over;
- over = 0;
- }
- }
- if (over) {
+ if (_activeVerb == 7 && over) {
_activeVerb = 13;
switchActor(_verbs[over].verbid - 1);
return;
}
setNewKidVerbs();
+ _activeVerb = 7;
return;
}
@@ -1200,7 +1183,7 @@ void ScummEngine_v0::checkExecVerbs() {
if (zone->number == kMainVirtScreen) {
// Ignore verbs?
- if (((ActorC64 *)a)->_miscflags & 0x40) {
+ if (a->_miscflags & 0x40) {
resetSentence(false);
return;
}
@@ -1464,9 +1447,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/sky/sky.h b/engines/sky/sky.h
index 7eff8c662a..58c9d1de11 100644
--- a/engines/sky/sky.h
+++ b/engines/sky/sky.h
@@ -35,8 +35,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Beneath a Steel Sky
*/
namespace Sky {
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
index 8d9ca85829..aee49c4b60 100644
--- a/engines/sword1/control.cpp
+++ b/engines/sword1/control.cpp
@@ -1118,8 +1118,7 @@ void Control::saveGameToFile(uint8 slot) {
outf->writeUint32BE(saveDate);
outf->writeUint16BE(saveTime);
- uint32 currentTime = _system->getMillis() / 1000;
- outf->writeUint32BE(currentTime - SwordEngine::_systemVars.engineStartTime);
+ outf->writeUint32BE(g_engine->getTotalPlayTime() / 1000);
_objMan->saveLiveList(liveBuf);
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
@@ -1181,10 +1180,9 @@ bool Control::restoreGameFromFile(uint8 slot) {
inf->readUint16BE(); // save time
if (saveVersion < 2) { // Before version 2 we didn't had play time feature
- SwordEngine::_systemVars.engineStartTime = _system->getMillis() / 1000; // Start counting
+ g_engine->setTotalPlayTime(0);
} else {
- uint32 currentTime = _system->getMillis() / 1000;
- SwordEngine::_systemVars.engineStartTime = currentTime - inf->readUint32BE(); // Engine start time
+ g_engine->setTotalPlayTime(inf->readUint32BE() * 1000);
}
_restoreBuf = (uint8*)malloc(
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index e51a3d6a8d..9fc24e75e8 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -309,12 +309,9 @@ SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int
desc.setSaveTime(hour, minutes);
if (versionSave > 1) {
- minutes = playTime / 60;
- hour = minutes / 60;
- minutes %= 60;
- desc.setPlayTime(hour, minutes);
+ desc.setPlayTime(playTime * 1000);
} else { //We have no playtime data
- desc.setPlayTime(0, 0);
+ desc.setPlayTime(0);
}
delete in;
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/resman.cpp b/engines/sword1/resman.cpp
index 41f952c3f4..228cd28bb7 100644
--- a/engines/sword1/resman.cpp
+++ b/engines/sword1/resman.cpp
@@ -332,10 +332,13 @@ Common::File *ResMan::resFile(uint32 id) {
Clu *closeClu = _openCluStart;
_openCluStart = _openCluStart->nextOpen;
- closeClu->file->close();
- delete closeClu->file;
- closeClu->file = NULL;
- closeClu->nextOpen = NULL;
+ if (closeClu) {
+ if (closeClu->file)
+ closeClu->file->close();
+ delete closeClu->file;
+ closeClu->file = NULL;
+ closeClu->nextOpen = NULL;
+ }
_openClus--;
}
}
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..9fa808d561 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);
@@ -886,8 +886,8 @@ uint8* Screen::psxBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint
// needed because some psx backgrounds are half width and half height
uint8* Screen::psxShrinkedBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres) {
- uint32 xresInTiles = (bakXres / 2) % 16 ? (bakXres / 32) + 1 : (bakXres / 32);
- uint32 yresInTiles = (bakYres / 2) % 16 ? (bakYres / 32) + 1 : (bakYres / 32);
+ uint32 xresInTiles = ((bakXres / 2) % 16) ? (bakXres / 32) + 1 : (bakXres / 32);
+ uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32);
uint32 totTiles = xresInTiles * yresInTiles;
uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid
uint32 tileXpos = 0;
@@ -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..ba1f77f896 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) {
@@ -555,7 +563,7 @@ void SwordEngine::checkCdFiles() { // check if we're running from cd, hdd or wha
Common::Error SwordEngine::go() {
_control->checkForOldSaveGames();
- SwordEngine::_systemVars.engineStartTime = _system->getMillis() / 1000;
+ setTotalPlayTime(0);
uint16 startPos = ConfMan.getInt("boot_param");
_control->readSavegameDescriptions();
diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h
index a0497847d5..f4e60e9fa3 100644
--- a/engines/sword1/sword1.h
+++ b/engines/sword1/sword1.h
@@ -36,8 +36,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Broken Sword: The Shadow of the Templars
*/
namespace Sword1 {
@@ -74,7 +74,6 @@ struct SystemVars {
uint8 showText;
uint8 language;
bool isDemo;
- uint32 engineStartTime; // Used for playtime
Common::Platform platform;
};
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/controls.cpp b/engines/sword2/controls.cpp
index 7428cbb534..c55cc72493 100644
--- a/engines/sword2/controls.cpp
+++ b/engines/sword2/controls.cpp
@@ -180,12 +180,12 @@ void FontRendererGui::fetchText(uint32 textId, byte *buf) {
byte *data = _vm->fetchTextLine(_vm->_resman->openResource(textId / SIZE), textId & 0xffff);
int i;
- for (i = 0; data[i + 2]; i++) {
- if (buf)
+ if (buf) {
+ for (i = 0; data[i + 2]; i++)
buf[i] = data[i + 2];
+ buf[i] = 0;
}
- buf[i] = 0;
_vm->_resman->closeResource(textId / SIZE);
}
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/music.cpp b/engines/sword2/music.cpp
index c052aa6b46..89073fef8e 100644
--- a/engines/sword2/music.cpp
+++ b/engines/sword2/music.cpp
@@ -496,9 +496,16 @@ int Sound::readBuffer(int16 *buffer, const int numSamples) {
memset(buffer, 0, 2 * numSamples);
if (!_mixBuffer || numSamples > _mixBufferLen) {
- if (_mixBuffer)
- _mixBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
- else
+ if (_mixBuffer) {
+ int16 *newBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
+ if (newBuffer) {
+ _mixBuffer = newBuffer;
+ } else {
+ // We can't use the old buffer any more. It's too small.
+ free(_mixBuffer);
+ _mixBuffer = 0;
+ }
+ } else
_mixBuffer = (int16 *)malloc(2 * numSamples);
_mixBufferLen = numSamples;
diff --git a/engines/sword2/protocol.cpp b/engines/sword2/protocol.cpp
index 03b021f04c..6c47c07401 100644
--- a/engines/sword2/protocol.cpp
+++ b/engines/sword2/protocol.cpp
@@ -412,8 +412,8 @@ byte *Sword2Engine::fetchPsxParallax(uint32 location, uint8 level) {
debug(2, "fetchPsxParallax() -> %s parallax, xRes: %u, yRes: %u", (level == 0) ? "Background" : "Foreground", plxXres, plxYres);
// Calculate the number of tiles which compose the parallax grid.
- horTiles = plxXres % 64 ? (plxXres / 64) + 1 : plxXres / 64;
- verTiles = plxYres % 16 ? (plxYres / 16) + 1 : plxYres / 16;
+ horTiles = (plxXres % 64) ? (plxXres / 64) + 1 : plxXres / 64;
+ verTiles = (plxYres % 16) ? (plxYres / 16) + 1 : plxYres / 16;
totSize = plxSize + horTiles * verTiles * 4 + 8;
diff --git a/engines/sword2/render.cpp b/engines/sword2/render.cpp
index 1a8dcdab16..99295be571 100644
--- a/engines/sword2/render.cpp
+++ b/engines/sword2/render.cpp
@@ -731,8 +731,8 @@ int32 Screen::initialisePsxParallaxLayer(byte *parallax) {
data = parallax + xTiles * yTiles * 4;
_xBlocks[_layer] = xTiles;
- _yBlocks[_layer] = (yTiles / 2) + (yTiles % 2 ? 1 : 0);
- bool oddTiles = (yTiles % 2 ? true : false);
+ _yBlocks[_layer] = (yTiles / 2) + ((yTiles % 2) ? 1 : 0);
+ bool oddTiles = ((yTiles % 2) ? true : false);
_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
if (!_blockSurfaces[_layer])
diff --git a/engines/sword2/resman.cpp b/engines/sword2/resman.cpp
index 81d74f355b..fa9c396ef3 100644
--- a/engines/sword2/resman.cpp
+++ b/engines/sword2/resman.cpp
@@ -302,6 +302,8 @@ byte *ResourceManager::openResource(uint32 res, bool dump) {
readCluIndex(cluFileNum, file);
}
+ assert(_resFiles[cluFileNum].entryTab);
+
uint32 pos = _resFiles[cluFileNum].entryTab[actual_res * 2 + 0];
uint32 len = _resFiles[cluFileNum].entryTab[actual_res * 2 + 1];
@@ -474,15 +476,18 @@ void ResourceManager::readCluIndex(uint16 fileNum, Common::File *file) {
file->seek(table_offset);
assert((tableSize % 8) == 0);
- _resFiles[fileNum].entryTab = (uint32*)malloc(tableSize);
+ _resFiles[fileNum].entryTab = (uint32 *)malloc(tableSize);
_resFiles[fileNum].numEntries = tableSize / 8;
+
+ assert(_resFiles[fileNum].entryTab);
+
file->read(_resFiles[fileNum].entryTab, tableSize);
if (file->eos() || file->err())
error("unable to read index table from file %s", _resFiles[fileNum].fileName);
#ifdef SCUMM_BIG_ENDIAN
for (int tabCnt = 0; tabCnt < _resFiles[fileNum].numEntries * 2; tabCnt++)
- _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]);
+ _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]);
#endif
}
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/sword2/sword2.h b/engines/sword2/sword2.h
index 201ce5e836..4fb856804b 100644
--- a/engines/sword2/sword2.h
+++ b/engines/sword2/sword2.h
@@ -53,8 +53,8 @@ class OSystem;
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Broken Sword II: The Smoking Mirror
*/
namespace Sword2 {
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
new file mode 100644
index 0000000000..3900df2fcf
--- /dev/null
+++ b/engines/sword25/detection.cpp
@@ -0,0 +1,160 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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 "common/savefile.h"
+#include "common/system.h"
+#include "engines/advancedDetector.h"
+
+#include "sword25/sword25.h"
+#include "sword25/kernel/persistenceservice.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;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); }
+ virtual SaveStateList listSaves(const char *target) const;
+};
+
+bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new Sword25::Sword25Engine(syst, desc);
+ }
+ return desc != 0;
+}
+
+bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves);
+}
+
+SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
+ Common::String pattern = target;
+ pattern = pattern + ".???";
+ SaveStateList saveList;
+
+ Sword25::PersistenceService ps;
+ Sword25::setGameTarget(target);
+
+ ps.reloadSlots();
+
+ for (uint i = 0; i < ps.getSlotCount(); ++i) {
+ if (ps.isSlotOccupied(i)) {
+ Common::String desc = ps.getSavegameDescription(i);
+ saveList.push_back(SaveStateDescriptor(i, desc));
+ }
+ }
+
+ return saveList;
+}
+
+#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..4193e02b2e
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -0,0 +1,228 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#define INDIRECTRENDERING 1
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "MOVIEPLAYER"
+
+#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */
+
+#ifdef USE_THEORADEC
+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();
+
+#if INDIRECTRENDERING
+ _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);
+#else
+ _backSurface = pGfx->getSurface();
+
+ _outX = (pGfx->getDisplayWidth() - _decoder.getWidth()) / 2;
+ _outY = (pGfx->getDisplayHeight() - _decoder.getHeight()) / 2;
+
+ if (_outX < 0)
+ _outX = 0;
+ if (_outY < 0)
+ _outY = 0;
+#endif
+
+ 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();
+ if (s) {
+ // Transfer the next frame
+ assert(s->bytesPerPixel == 4);
+
+#if INDIRECTRENDERING
+ byte *frameData = (byte *)s->getBasePtr(0, 0);
+ _outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
+#else
+ g_system->copyRectToScreen((byte *)s->getBasePtr(0, 0), s->pitch, _outX, _outY, MIN(s->w, _backSurface->w), MIN(s->h, _backSurface->h));
+ g_system->updateScreen();
+#endif
+ } else {
+ // Movie complete, so unload the movie
+ unloadMovie();
+ }
+ }
+}
+
+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;
+}
+
+#else // USE_THEORADEC
+
+MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+MoviePlayer::~MoviePlayer() {
+}
+
+bool MoviePlayer::loadMovie(const Common::String &Filename, unsigned int Z) {
+ return true;
+}
+
+bool MoviePlayer::unloadMovie() {
+ return true;
+}
+
+bool MoviePlayer::play() {
+ return true;
+}
+
+bool MoviePlayer::pause() {
+ return true;
+}
+
+void MoviePlayer::update() {
+}
+
+bool MoviePlayer::isMovieLoaded() {
+ return true;
+}
+
+bool MoviePlayer::isPaused() {
+ return true;
+}
+
+float MoviePlayer::getScaleFactor() {
+ return 1.0f;
+}
+
+void MoviePlayer::setScaleFactor(float ScaleFactor) {
+}
+
+double MoviePlayer::getTime() {
+ return 1.0;
+}
+
+#endif // USE_THEORADEC
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
new file mode 100644
index 0000000000..350407cea5
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.h
@@ -0,0 +1,156 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "common/scummsys.h" // for USE_THEORADEC
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/gfx/bitmap.h"
+
+#ifdef USE_THEORADEC
+#include "sword25/fmv/theora_decoder.h"
+#endif
+
+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();
+
+
+#ifdef USE_THEORADEC
+ TheoraDecoder _decoder;
+
+ Graphics::Surface *_backSurface;
+ int _outX, _outY;
+
+ RenderObjectPtr<Bitmap> _outputBitmap;
+#endif
+};
+
+} // 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..aa854448ff
--- /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 "common/scummsys.h" // for USE_THEORADEC
+
+#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() {
+ ScriptEngine *pScript = Kernel::getInstance()->getScript();
+ 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..d211136614
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -0,0 +1,499 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#ifdef USE_THEORADEC
+#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, 32767);
+ _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()) {
+ return NULL;
+ }
+
+ 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.
+ if (_stateFlag && _audiobufReady) {
+ _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+
+ // The audio mixer is now responsible for the old audio buffer.
+ // We need to create a new one.
+ _audiobuf = (ogg_int16_t *)calloc(AUDIOFD_FRAGSIZE, sizeof(ogg_int16_t));
+ _audiobufFill = 0;
+ _audiobufReady = false;
+ }
+
+ // 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
+
+#endif
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
new file mode 100644
index 0000000000..f6c622563b
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -0,0 +1,158 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 "common/scummsys.h" // for USE_THEORADEC
+
+#ifdef USE_THEORADEC
+
+#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
+
+#endif
diff --git a/engines/sword25/fmv/yuvtorgba.cpp b/engines/sword25/fmv/yuvtorgba.cpp
new file mode 100644
index 0000000000..e9d0189265
--- /dev/null
+++ b/engines/sword25/fmv/yuvtorgba.cpp
@@ -0,0 +1,247 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#ifdef USE_THEORADEC
+
+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
+
+#endif
diff --git a/engines/sword25/fmv/yuvtorgba.h b/engines/sword25/fmv/yuvtorgba.h
new file mode 100644
index 0000000000..675248e6e0
--- /dev/null
+++ b/engines/sword25/fmv/yuvtorgba.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_YUVTORGBA_H
+#define SWORD25_YUVTORGBA_H
+
+#include "common/scummsys.h" // for USE_THEORADEC
+
+#ifdef USE_THEORADEC
+#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
+
+#endif
diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp
new file mode 100644
index 0000000000..0d3baae347
--- /dev/null
+++ b/engines/sword25/gfx/animation.cpp
@@ -0,0 +1,680 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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();
+ }
+
+ // Invoke the "delete" callback
+ if (_deleteCallback)
+ (_deleteCallback)(getHandle());
+
+}
+
+void Animation::play() {
+ // If the animation was completed, then play it again from the start.
+ 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 = Kernel::getInstance()->getGfx();
+ 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);
+ }
+
+ // Deal with overflows
+ if (tmpCurFrame < 0) {
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
+
+ // An underflow may only occur if the animation type is JOJO.
+ BS_ASSERT(animationDescriptionPtr->getAnimationType() == AT_JOJO);
+ tmpCurFrame = - tmpCurFrame;
+ _direction = FORWARD;
+ } else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
+ // Loop-Point callback
+ if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
+ _loopPointCallback = 0;
+
+ 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 callback
+ if (_actionCallback && !(_actionCallback)(getHandle()))
+ _actionCallback = 0;
+ }
+ }
+
+ _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;
+}
+
+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.writeString(_animationResourcePtr->getFileName());
+ } else if (_animationTemplateHandle) {
+ uint marker = 1;
+ writer.write(marker);
+ writer.write(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ //writer.write(_AnimationDescriptionPtr);
+
+ writer.write(_framesLocked);
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaLoopPointCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaActionCB");
+ writer.write(getHandle());
+ writer.write((uint)1);
+ writer.writeString("LuaDeleteCB");
+ writer.write(getHandle());
+
+ 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.readString(resourceFilename);
+ initializeAnimationResource(resourceFilename);
+ } else if (marker == 1) {
+ reader.read(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ reader.read(_framesLocked);
+ if (_framesLocked)
+ lockAllFrames();
+
+
+ // The following is only there to for compatibility with older saves
+ // resp. the original engine.
+ uint callbackCount;
+ Common::String callbackFunctionName;
+ uint callbackData;
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaLoopPointCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaActionCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // loop point callback
+ reader.read(callbackCount);
+ assert(callbackCount == 1);
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaDeleteCB");
+ reader.read(callbackData);
+ assert(callbackData == getHandle());
+
+ // Set the callbacks
+ setCallbacks();
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+// -----------------------------------------------------------------------------
+
+AnimationDescription *Animation::getAnimationDescription() const {
+ if (_animationResourcePtr)
+ return _animationResourcePtr;
+ else
+ return AnimationTemplateRegistry::instance().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..72fe7e2de8
--- /dev/null
+++ b/engines/sword25/gfx/animation.h
@@ -0,0 +1,219 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * 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 setCallbacks();
+
+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;
+
+ ANIMATION_CALLBACK _loopPointCallback;
+ ANIMATION_CALLBACK _actionCallback;
+ ANIMATION_CALLBACK _deleteCallback;
+
+ /**
+ @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();
+ 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..88cbb23503
--- /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..6e5f683a2e
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -0,0 +1,252 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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
+ _pPackage = Kernel::getInstance()->getPackage();
+ 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"], 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..4a060dbad9
--- /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::instance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+uint AnimationTemplate::create(const AnimationTemplate &other) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(other);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::instance().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::instance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::instance().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::instance().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::instance().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::instance().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.writeString(Iter->fileName);
+ writer.writeString(Iter->action);
+ ++Iter;
+ }
+
+ // Restliche Member persistieren.
+ writer.writeString(_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.readString(frame.fileName);
+ reader.readString(frame.action);
+
+ _frames.push_back(frame);
+ }
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ Common::String sourceAnimation;
+ reader.readString(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..ac1d6096f4
--- /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"
+
+DECLARE_SINGLETON(Sword25::AnimationTemplateRegistry)
+
+namespace Sword25 {
+
+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..c5308bb124
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/singleton.h"
+
+namespace Sword25 {
+
+class AnimationTemplate;
+
+class AnimationTemplateRegistry :
+ public ObjectRegistry<AnimationTemplate>,
+ public Persistable,
+ public Common::Singleton<AnimationTemplateRegistry> {
+public:
+
+ 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;
+};
+
+} // 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..b5412c8276
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.cpp
@@ -0,0 +1,183 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/bitmap.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "BITMAP"
+
+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() {
+}
+
+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;
+ if (_width <= 0)
+ _width = 1;
+ 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;
+ if (_height <= 0)
+ _height = 1;
+ forceRefresh();
+ }
+}
+
+void Bitmap::setFlipH(bool flipH) {
+ _flipH = flipH;
+ forceRefresh();
+}
+
+void Bitmap::setFlipV(bool flipV) {
+ _flipV = flipV;
+ forceRefresh();
+}
+
+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..741269c423
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.h
@@ -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
+ *
+ */
+
+#ifndef SWORD25_BITMAP_H
+#define SWORD25_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+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..cf3c85e3ac
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.cpp
@@ -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
+ *
+ */
+
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "BITMAP"
+
+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..37849f918e
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.h
@@ -0,0 +1,212 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#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..612e370712
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.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$
+ *
+ */
+
+/*
+ * 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/dynamicbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "DYNAMICBITMAP"
+
+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 = Kernel::getInstance()->getGfx();
+ 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);
+}
+
+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;
+}
+
+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..1737bdf5fc
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.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_DYNAMIC_BITMAP_H
+#define SWORD25_DYNAMIC_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+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..dbb9c67fe5
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -0,0 +1,138 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+
+#include "sword25/gfx/fontresource.h"
+
+namespace Sword25 {
+
+enum {
+ DEFAULT_LINEHEIGHT = 20,
+ DEFAULT_GAPWIDTH = 1
+};
+
+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 = _pKernel->getPackage();
+ 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"], 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"], 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 = _pKernel->getPackage();
+ 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"], 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"], 1, &top) || (top < 0)) {
+ return parserError("Illegal or missing top attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["left"], 1, &left) || (left < 0)) {
+ return parserError("Illegal or missing left attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["right"], 1, &right) || (right < 0)) {
+ return parserError("Illegal or missing right attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["bottom"], 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..19c44d0ade
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.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 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
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "common/xmlparser.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+
+namespace Sword25 {
+
+class Kernel;
+
+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);
+};
+
+} // 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..07415cc2dc
--- /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) :
+ _FPS(0),
+ _FPSCount(0),
+ _lastUpdateTime(-1) {
+ setUpdateFrequency(updateFrequency);
+}
+
+void Framecounter::update() {
+ // Aktuellen Systemtimerstand auslesen
+ uint64 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 (_lastUpdateTime == -1)
+ _lastUpdateTime = timer;
+ else {
+ // Die Anzahl der Frames im aktuellen Messzeitraum wird erhöht.
+ _FPSCount++;
+
+ // Falls der Messzeitraum verstrichen ist, wird die durchschnittliche Framerate berechnet und ein neuer Messzeitraum begonnen.
+ if (timer - _lastUpdateTime >= _updateDelay) {
+ _FPS = static_cast<int>((1000000 * (uint64)_FPSCount) / (timer - _lastUpdateTime));
+ _lastUpdateTime = timer;
+ _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..994950573f
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.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$
+ *
+ */
+
+/*
+ * 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"
+
+namespace Sword25 {
+
+/**
+ * A simple class that implements a frame counter
+ */
+class Framecounter {
+private:
+
+ // TODO: This class should be rewritten based on Audio::Timestamp,
+ // which provides higher accuracy and avoids using 64 bit data types.
+ typedef unsigned long long uint64;
+ typedef signed long long int64;
+
+ 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 _FPS;
+ }
+
+private:
+ int _FPS;
+ int _FPSCount;
+ int64 _lastUpdateTime;
+ uint64 _updateDelay;
+};
+
+// Inlines
+void Framecounter::setUpdateFrequency(int updateFrequency) {
+ // Frequency in time (converted to microseconds)
+ _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..f629993abf
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.cpp
@@ -0,0 +1,494 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+enum {
+ BIT_DEPTH = 32,
+ BACKBUFFER_COUNT = 1
+};
+
+
+namespace Sword25 {
+
+static const uint FRAMETIME_SAMPLE_COUNT = 5; // Anzahl der Framezeiten über die, die Framezeit gemittelt wird
+
+GraphicEngine::GraphicEngine(Kernel *pKernel) :
+ _width(0),
+ _height(0),
+ _bitDepth(0),
+ _windowed(0),
+ _lastTimeStamp((uint) -1), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
+ _lastFrameDuration(0),
+ _timerActive(true),
+ _frameTimeSampleSlot(0),
+ _repaintedPixels(0),
+ _thumbnail(NULL),
+ ResourceService(pKernel) {
+ _frameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
+
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+GraphicEngine::~GraphicEngine() {
+ unregisterScriptBindings();
+ _backSurface.free();
+ _frameBuffer.free();
+ delete _thumbnail;
+}
+
+bool GraphicEngine::init(int width, int height, int bitDepth, int backbufferCount, bool isWindowed_) {
+ // 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);
+ _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
+ _width = width;
+ _height = height;
+ _bitDepth = bitDepth;
+ _windowed = isWindowed_;
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = _width;
+ _screenRect.bottom = _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
+ _mainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(width, height, BS_ARGB(0, 0, 0, 0));
+ if (!_mainPanelPtr.isValid())
+ return false;
+ _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 (!_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");
+
+ _debugLines.clear();
+ }
+
+ // Framecounter aktualisieren
+ _FPSCounter.update();
+
+ return true;
+}
+
+RenderObjectPtr<Panel> GraphicEngine::getMainPanel() {
+ return _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(_width - 1, _height - 1);
+
+ int ca = (color >> 24) & 0xff;
+
+ if (ca == 0)
+ return true;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
+
+ if (fillRectPtr) {
+ rect = *fillRectPtr;
+ }
+
+ if (rect.width() > 0 && rect.height() > 0) {
+ if (ca == 0xff) {
+ _backSurface.fillRect(rect, color);
+ } else {
+ byte *outo = (byte *)_backSurface.getBasePtr(rect.left, rect.top);
+ byte *out;
+
+ for (int i = rect.top; i < rect.bottom; i++) {
+ out = outo;
+ for (int j = rect.left; j < rect.right; j++) {
+ *out += (byte)(((cb - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cg - *out) * ca) >> 8);
+ out++;
+ *out += (byte)(((cr - *out) * ca) >> 8);
+ out++;
+ *out = 255;
+ out++;
+ }
+
+ outo += _backSurface.pitch;
+ }
+ }
+
+ g_system->copyRectToScreen((byte *)_backSurface.getBasePtr(rect.left, rect.top), _backSurface.pitch, rect.left, rect.top, rect.width(), rect.height());
+ }
+
+ return true;
+}
+
+Graphics::Surface *GraphicEngine::getScreenshot() {
+ return &_frameBuffer;
+}
+
+// -----------------------------------------------------------------------------
+// RESOURCE MANAGING
+// -----------------------------------------------------------------------------
+
+Resource *GraphicEngine::loadResource(const Common::String &filename) {
+ BS_ASSERT(canLoadResource(filename));
+
+ // Load image for "software buffer" (FIXME: Whatever that means?)
+ if (filename.hasSuffix("_s.png")) {
+ 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;
+ }
+
+ // Load sprite image
+ if (filename.hasSuffix(".png") || filename.hasSuffix(".b25s")) {
+ 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;
+ }
+
+
+ // Load vector graphics
+ if (filename.hasSuffix(".swf")) {
+ 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;
+ }
+
+ // Load animation
+ if (filename.hasSuffix("_ani.xml")) {
+ AnimationResource *pResource = new AnimationResource(filename);
+ if (pResource->isValid())
+ return pResource;
+ else {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ // Load font
+ if (filename.hasSuffix("_fnt.xml")) {
+ 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") ||
+ filename.hasSuffix("_ani.xml") ||
+ filename.hasSuffix("_fnt.xml") ||
+ filename.hasSuffix(".swf") ||
+ filename.hasSuffix(".b25s");
+}
+
+
+// -----------------------------------------------------------------------------
+// DEBUGGING
+// -----------------------------------------------------------------------------
+
+void GraphicEngine::drawDebugLine(const Vertex &start, const Vertex &end, uint color) {
+ _debugLines.push_back(DebugLine(start, end, color));
+}
+
+void GraphicEngine::updateLastFrameDuration() {
+ // Record current time
+ const uint currentTime = Kernel::getInstance()->getMilliTicks();
+
+ // Compute the elapsed time since the last frame and prevent too big ( > 250 msecs) time jumps.
+ // These can occur when loading save states, during debugging or due to hardware inaccuracies.
+ _frameTimeSamples[_frameTimeSampleSlot] = static_cast<uint>(currentTime - _lastTimeStamp);
+ if (_frameTimeSamples[_frameTimeSampleSlot] > 250000)
+ _frameTimeSamples[_frameTimeSampleSlot] = 250000;
+ _frameTimeSampleSlot = (_frameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
+
+ // Compute the average frame duration over multiple frames to eliminate outliers.
+ Common::Array<uint>::const_iterator it = _frameTimeSamples.begin();
+ uint sum = *it;
+ for (it++; it != _frameTimeSamples.end(); it++)
+ sum += *it;
+ _lastFrameDuration = sum * 1000 / FRAMETIME_SAMPLE_COUNT;
+
+ // Update m_LastTimeStamp with the current frame's timestamp
+ _lastTimeStamp = currentTime;
+}
+
+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");
+
+ // Red color component reading
+ 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);
+
+ // Green color component reading
+ 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);
+
+ // Blue color component reading
+ 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 color component reading
+ 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(_timerActive);
+
+ bool result = _renderObjectManagerPtr->persist(writer);
+
+ return result;
+}
+
+bool GraphicEngine::unpersist(InputPersistenceBlock &reader) {
+ reader.read(_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..e1ae0d2d62
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.h
@@ -0,0 +1,392 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+ *
+ */
+
+/*
+ * 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/ptr.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+#include "sword25/kernel/common.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;
+
+typedef uint BS_COLOR;
+
+#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))
+
+/**
+ * This is the graphics engine. Unlike the original code, this is not
+ * an interface that needs to be subclassed, but rather already contains
+ * all required functionality.
+ */
+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.
+ * @note 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 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() const {
+ if (!_timerActive)
+ return 0;
+ return _lastFrameDuration;
+ }
+
+ /**
+ * Specifies the time (in microseconds) the previous frame took
+ */
+ float getLastFrameDuration() const {
+ if (!_timerActive)
+ return 0;
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
+ }
+
+ void stopMainTimer() {
+ _timerActive = false;
+ }
+
+ void resumeMainTimer() {
+ _timerActive = true;
+ }
+
+ float getSecondaryFrameDuration() const {
+ return static_cast<float>(_lastFrameDuration) / 1000000.0f;
+ }
+
+ // Accessor methods
+
+ /**
+ * Returns the width of the output buffer in pixels
+ */
+ int getDisplayWidth() const {
+ return _width;
+ }
+
+ /**
+ * Returns the height of the output buffer in pixels
+ */
+ int getDisplayHeight() const {
+ return _height;
+ }
+
+ /**
+ * Returns the bounding box of the output buffer: (0, 0, Width, Height)
+ */
+ Common::Rect &getDisplayRect() {
+ return _screenRect;
+ }
+
+ /**
+ * Returns the bit depth of the output buffer
+ */
+ int getBitDepth() {
+ return _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 _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)
+ * @note FIf the rectangle is not completely inside the screen, it is automatically clipped.
+ */
+ bool fill(const Common::Rect *fillRectPtr = 0, uint color = BS_RGB(0, 0, 0));
+
+ // Debugging Methods
+
+ int getFPSCount() const {
+ return _FPSCounter.getFPS();
+ }
+ int getRepaintedPixels() const {
+ return _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 _width;
+ int _height;
+ Common::Rect _screenRect;
+ int _bitDepth;
+ bool _windowed;
+
+ // Debugging Variables
+ // -------------------
+ Framecounter _FPSCounter;
+
+ uint _repaintedPixels;
+
+ /**
+ * Calculates the time since the last frame beginning has passed.
+ */
+ void updateLastFrameDuration();
+
+private:
+ bool registerScriptBindings();
+ void unregisterScriptBindings();
+
+ // LastFrameDuration Variables
+ // ---------------------------
+ uint _lastTimeStamp;
+ uint _lastFrameDuration;
+ bool _timerActive;
+ Common::Array<uint> _frameTimeSamples;
+ uint _frameTimeSampleSlot;
+
+private:
+ byte *_backBuffer;
+
+ RenderObjectPtr<Panel> _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> _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..0814a23871
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine_script.cpp
@@ -0,0 +1,1305 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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"
+
+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;
+ }
+};
+
+static LuaCallback *loopPointCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
+static ActionCallback *actionCallbackPtr = 0; // FIXME: should be turned into GraphicEngine member var
+}
+
+// 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::instance().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::instance().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 = pKernel->getGfx();
+ 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) {
+ // This is used by system/debug.lua only. We do not implement this; support
+ // for taking screenshots is a backend feature.
+ lua_pushbooleancpp(L, false);
+
+ 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->setCallbacks();
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+void Animation::setCallbacks() {
+ _actionCallback = animationActionCallback;
+ _loopPointCallback = animationLoopPointCallback;
+ _deleteCallback = animationDeleteCallback;
+}
+
+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 = pKernel->getScript();
+ 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;
+
+ assert(loopPointCallbackPtr == 0);
+ loopPointCallbackPtr = new LuaCallback(L);
+
+ assert(actionCallbackPtr == 0);
+ actionCallbackPtr = new ActionCallback(L);
+
+ return true;
+}
+
+void GraphicEngine::unregisterScriptBindings() {
+ delete loopPointCallbackPtr;
+ loopPointCallbackPtr = 0;
+
+ delete actionCallbackPtr;
+ actionCallbackPtr = 0;
+}
+
+} // 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..064ca333e7
--- /dev/null
+++ b/engines/sword25/gfx/image/art.cpp
@@ -0,0 +1,2653 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 = (int)ceil((th_0 - th_1) / theta);
+ } else {
+ /* curve to the right */
+ if (th_1 < th_0) th_1 += M_PI * 2;
+ n_pts = (int)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 = (int)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 = 0, right_filled = 0;
+ 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 = (int)((ix_min + 1 - (x_min + x_max) * 0.5) * delta);
+
+ ADD_STEP(ix_min, xdelta)
+
+ if (ix_min + 1 < x1) {
+ xdelta = (int)(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 =
+ (int)(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_ = (int)((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_ =
+ (int)(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 = (int)(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/image.h b/engines/sword25/gfx/image/image.h
new file mode 100644
index 0000000000..5ac6d1ac25
--- /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/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp
new file mode 100644
index 0000000000..1b72595a8f
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.cpp
@@ -0,0 +1,255 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+ *
+ */
+
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/image/pngloader.h"
+#include <png.h>
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "PNGLOADER"
+
+
+/**
+ * Load a NULL-terminated string from the given stream.
+ */
+static Common::String loadString(Common::ReadStream &in, uint maxSize = 999) {
+ Common::String result;
+
+ while (!in.eos() && (result.size() < maxSize)) {
+ char ch = (char)in.readByte();
+ if (ch == '\0')
+ break;
+
+ result += ch;
+ }
+
+ return result;
+}
+
+/**
+ * Check if the given data is a savegame, and if so, locate the
+ * offset to the image data.
+ * @return offset to image data if fileDataPtr contains a savegame; 0 otherwise
+ */
+static uint findEmbeddedPNG(const byte *fileDataPtr, uint fileSize) {
+ if (fileSize < 100)
+ return 0;
+ if (memcmp(fileDataPtr, "BS25SAVEGAME", 12))
+ return 0;
+
+ // Read in the header
+ Common::MemoryReadStream stream(fileDataPtr, fileSize);
+ stream.seek(0, SEEK_SET);
+
+ // Headerinformationen der Spielstandes einlesen.
+ uint compressedGamedataSize;
+ loadString(stream); // Marker
+ loadString(stream); // Version
+ loadString(stream); // Description
+ 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);
+}
+
+static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
+ const byte **ref = (const byte **)png_get_io_ptr(png_ptr);
+ memcpy(data, *ref, length);
+ *ref += length;
+}
+
+static bool doIsCorrectImageFormat(const byte *fileDataPtr, uint fileSize) {
+ return (fileSize > 8) && png_check_sig(const_cast<byte *>(fileDataPtr), 8);
+}
+
+
+bool PNGLoader::doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ int bitDepth;
+ int colorType;
+ int interlaceType;
+ int i;
+
+ // Check for valid PNG signature
+ if (!doIsCorrectImageFormat(fileDataPtr, fileSize)) {
+ 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
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, 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(GraphicEngine::CF_ARGB32, 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);
+
+ if (interlaceType == PNG_INTERLACE_NONE) {
+ // PNGs without interlacing can simply be read row by row.
+ for (i = 0; i < height; i++) {
+ png_read_row(png_ptr, uncompressedDataPtr + i * pitch, NULL);
+ }
+ } else {
+ // PNGs with interlacing require us to allocate an auxillary
+ // buffer with pointers to all row starts.
+
+ // Allocate row pointer buffer
+ png_bytep *pRowPtr = new png_bytep[height];
+ if (!pRowPtr) {
+ error("Could not allocate memory for row pointers.");
+ }
+
+ // Initialize row pointers
+ for (i = 0; i < height; i++)
+ pRowPtr[i] = uncompressedDataPtr + i * pitch;
+
+ // Read image data
+ png_read_image(png_ptr, pRowPtr);
+
+ // Free row pointer buffer
+ delete[] pRowPtr;
+ }
+
+ // Read additional data at the end.
+ png_read_end(png_ptr, NULL);
+
+ // Destroy libpng structures
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ // Signal success
+ return true;
+}
+
+bool PNGLoader::decodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doDecodeImage(fileDataPtr + pngOffset, fileSize - pngOffset, uncompressedDataPtr, width, height, pitch);
+}
+
+bool PNGLoader::doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ // Check for valid PNG signature
+ 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
+ const byte **ref = &fileDataPtr;
+ png_set_read_fn(png_ptr, (void *)ref, 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);
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ return true;
+
+}
+
+bool PNGLoader::imageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height) {
+ uint pngOffset = findEmbeddedPNG(fileDataPtr, fileSize);
+ return doImageProperties(fileDataPtr + pngOffset, fileSize - pngOffset, width, height);
+}
+
+
+} // 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..e0d68ff8b9
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.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_PNGLOADER2_H
+#define SWORD25_PNGLOADER2_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+/**
+ * Class for loading PNG files, and PNG data embedded into savegames.
+ *
+ * Originally written by Malte Thiesen.
+ */
+class PNGLoader {
+protected:
+ PNGLoader() {} // Protected constructor to prevent instances
+
+ static bool doDecodeImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch);
+ static bool doImageProperties(const byte *fileDataPtr, uint fileSize, int &width, int &height);
+
+public:
+
+ /**
+ * Decode an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] pUncompressedData if successful, this is set to a pointer containing the decoded image data
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @param[out] pitch if successful, this is set to the number of bytes per scanline in the image
+ * @return false in case of an error
+ *
+ * @remark The size of the output data equals pitch * height.
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool decodeImage(const byte *pFileData, uint fileSize,
+ byte *&pUncompressedData,
+ int &width, int &height,
+ int &pitch);
+ /**
+ * Extract the properties of an image.
+ * @param[in] fileDatePtr pointer to the image data
+ * @param[in] fileSize size of the image data in bytes
+ * @param[out] width if successful, this is set to the width of the image
+ * @param[out] height if successful, this is set to the height of the image
+ * @return returns true if extraction of the properties was successful, false in case of an error
+ *
+ * @remark This function does not free the image buffer passed to it,
+ * it is the callers responsibility to do so.
+ */
+ static bool imageProperties(const byte *fileDatePtr, uint fileSize,
+ 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..9392eca044
--- /dev/null
+++ b/engines/sword25/gfx/image/renderedimage.cpp
@@ -0,0 +1,397 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/pngloader.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 = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPackage);
+
+ _backSurface = Kernel::getInstance()->getGfx()->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
+ int pitch;
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ delete[] pFileData;
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ if (!PNGLoader::decodeImage(pFileData, fileSize, _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 = Kernel::getInstance()->getGfx()->getSurface();
+
+ _doCleanup = true;
+
+ result = true;
+ return;
+}
+
+RenderedImage::RenderedImage() : _width(0), _height(0), _data(0) {
+ _backSurface = Kernel::getInstance()->getGfx()->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..f0a8899bb6
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.cpp
@@ -0,0 +1,115 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/package/packagemanager.h"
+#include "sword25/gfx/image/pngloader.h"
+#include "sword25/gfx/image/swimage.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "SWIMAGE"
+
+
+SWImage::SWImage(const Common::String &filename, bool &result) :
+ _imageDataPtr(0),
+ _width(0),
+ _height(0) {
+ result = false;
+
+ PackageManager *pPackage = Kernel::getInstance()->getPackage();
+ 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
+ int pitch;
+ if (!PNGLoader::imageProperties(pFileData, fileSize, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ byte *pUncompressedData;
+ if (!PNGLoader::decodeImage(pFileData, fileSize, 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..a914c4f41f
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.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$
+ *
+ */
+
+/*
+ * 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
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/graphicengine.h"
+
+
+namespace Sword25 {
+
+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..5c15c4771a
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.cpp
@@ -0,0 +1,636 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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..43ac8683ac
--- /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 = 0;
+ while (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);
+ art_svp_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..2de1de133a
--- /dev/null
+++ b/engines/sword25/gfx/panel.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$
+ *
+ */
+
+/*
+ * 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/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"
+
+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() {
+}
+
+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 = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
+
+ return gfxPtr->fill(&_bbox, _color);
+}
+
+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..6fe96369a6
--- /dev/null
+++ b/engines/sword25/gfx/panel.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$
+ *
+ */
+
+/*
+ * 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
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+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..77af0bee92
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -0,0 +1,517 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+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::instance().registerObject(this);
+ else
+ _handle = RenderObjectRegistry::instance().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::instance().deregisterObject(this);
+}
+
+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;
+}
+
+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;
+}
+
+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();
+}
+
+bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
+ result = pObject->getBbox();
+ result.clip(_bbox);
+ return result.isValidRect();
+}
+
+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;
+}
+
+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>();
+ }
+}
+
+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;
+}
+
+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..7b7b9047f7
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.h
@@ -0,0 +1,525 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#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 {
+
+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..94f7a04b45
--- /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::instance().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::instance().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..8511382d6e
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.h
@@ -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
+ *
+ */
+
+/*
+ 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
+
+#include "common/rect.h"
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobjectptr.h"
+#include "sword25/kernel/persistable.h"
+
+namespace Sword25 {
+
+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..c22c6e83e7
--- /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::instance().resolveHandle(_handle));
+ }
+
+ bool operator==(const RenderObjectPtr<T> & other) {
+ return _handle == other._handle;
+ }
+
+ bool isValid() const {
+ return RenderObjectRegistry::instance().resolveHandle(_handle) != 0;
+ }
+
+ void erase() {
+ delete static_cast<T *>(RenderObjectRegistry::instance().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..c52e2f1a45
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.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 Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobjectregistry.h"
+
+DECLARE_SINGLETON(Sword25::RenderObjectRegistry)
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECTREGISTRY"
+
+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..357d041068
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.h
@@ -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 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
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/objectregistry.h"
+
+#include "common/singleton.h"
+
+namespace Sword25 {
+
+class RenderObject;
+
+class RenderObjectRegistry :
+ public ObjectRegistry<RenderObject>,
+ public Common::Singleton<RenderObjectRegistry> {
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+};
+
+} // 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..88417b72c5
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.cpp
@@ -0,0 +1,185 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+ *
+ */
+
+// Disable symbol overrides so that we can use png.h
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#define BS_LOG_PREFIX "SCREENSHOT"
+
+#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_get_io_ptr(png_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..eefaa1bca6
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.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_SCREENSHOT_H
+#define SWORD25_SCREENSHOT_H
+
+#include "graphics/surface.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+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..3019fe0ac1
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.cpp
@@ -0,0 +1,185 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/staticbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "STATICBITMAP"
+
+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 = Kernel::getInstance()->getGfx();
+ 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;
+}
+
+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;
+}
+
+bool StaticBitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= Bitmap::persist(writer);
+ writer.writeString(_resourceFilename);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool StaticBitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= Bitmap::unpersist(reader);
+ Common::String resourceFilename;
+ reader.readString(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..b5b4c4f5a2
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.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_STATIC_BITMAP_H
+#define SWORD25_STATIC_BITMAP_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+
+namespace Sword25 {
+
+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..b8e12ba570
--- /dev/null
+++ b/engines/sword25/gfx/text.cpp
@@ -0,0 +1,358 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#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"
+
+namespace {
+const uint AUTO_WRAP_THRESHOLD_DEFAULT = 300;
+}
+
+Text::Text(RenderObjectPtr<RenderObject> parentPtr) :
+ RenderObject(parentPtr, RenderObject::TYPE_TEXT),
+ _modulationColor(0xffffffff),
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+
+}
+
+Text::Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ RenderObject(parentPtr, TYPE_TEXT, handle),
+ // Temporarily set fields prior to unpersisting actual values
+ _modulationColor(0xffffffff),
+ _autoWrap(false),
+ _autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+
+ // Unpersist the fields
+ _initSuccess = unpersist(reader);
+}
+
+bool Text::setFont(const Common::String &font) {
+ // Font precachen.
+ if (getResourceManager()->precacheResource(font)) {
+ _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) {
+ _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 != _autoWrap) {
+ _autoWrap = autoWrap;
+ updateFormat();
+ forceRefresh();
+ }
+}
+
+void Text::setAutoWrapThreshold(uint autoWrapThreshold) {
+ if (autoWrapThreshold != _autoWrapThreshold) {
+ _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 = Kernel::getInstance()->getGfx();
+ BS_ASSERT(gfxPtr);
+
+ bool result = true;
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _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(_font);
+ if (!resourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", _font.c_str());
+ return NULL;
+ }
+ if (resourcePtr->getType() != Resource::TYPE_FONT) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", _font.c_str());
+ return NULL;
+ }
+
+ fontPtr = static_cast<FontResource *>(resourcePtr);
+ }
+
+ return fontPtr;
+}
+
+void Text::updateFormat() {
+ FontResource *fontPtr = lockFontResource();
+ BS_ASSERT(fontPtr);
+
+ updateMetrics(*fontPtr);
+
+ _lines.resize(1);
+ if (_autoWrap && (uint) _width >= _autoWrapThreshold && _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...
+ _lines[0].text = "";
+ for (uint i = 0; i < _text.size(); ++i) {
+ uint j;
+ tempLineWidth = 0;
+ lastSpace = 0;
+ for (j = i; j < _text.size(); ++j) {
+ if ((byte)_text[j] == ' ')
+ lastSpace = j;
+
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ tempLineWidth += curCharRect.width();
+ tempLineWidth += fontPtr->getGapWidth();
+
+ if ((tempLineWidth >= _autoWrapThreshold) && (lastSpace > 0))
+ break;
+ }
+
+ if (j == _text.size()) // everything in 1 line.
+ lastSpace = _text.size();
+
+ curLineWidth = 0;
+ curLineHeight = 0;
+ for (j = i; j < lastSpace; ++j) {
+ _lines[curLine].text += _text[j];
+
+ const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
+ curLineWidth += curCharRect.width();
+ curLineWidth += fontPtr->getGapWidth();
+ if ((uint)curCharRect.height() > curLineHeight)
+ curLineHeight = curCharRect.height();
+ }
+
+ _lines[curLine].bbox.right = curLineWidth;
+ _lines[curLine].bbox.bottom = curLineHeight;
+ if ((uint)_width < curLineWidth)
+ _width = curLineWidth;
+
+ if (lastSpace < _text.size()) {
+ ++curLine;
+ BS_ASSERT(curLine == _lines.size());
+ _lines.resize(curLine + 1);
+ _lines[curLine].text = "";
+ }
+
+ i = lastSpace;
+ }
+
+ // Bounding-Box der einzelnen Zeilen relativ zur ersten festlegen (vor allem zentrieren).
+ _height = 0;
+ Common::Array<Line>::iterator iter = _lines.begin();
+ for (; iter != _lines.end(); ++iter) {
+ Common::Rect &bbox = (*iter).bbox;
+ bbox.left = (_width - bbox.right) / 2;
+ bbox.right = bbox.left + bbox.right;
+ bbox.top = (iter - _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.
+ _lines[0].text = _text;
+ _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 < _text.size(); ++i) {
+ const Common::Rect &curRect = fontResource.getCharacterRect((byte)_text[i]);
+ _width += curRect.width();
+ if (i != _text.size() - 1)
+ _width += fontResource.getGapWidth();
+ if (_height < curRect.height())
+ _height = curRect.height();
+ }
+}
+
+bool Text::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+
+ writer.write(_modulationColor);
+ writer.writeString(_font);
+ writer.writeString(_text);
+ writer.write(_autoWrap);
+ writer.write(_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.readString(font);
+ setFont(font);
+
+ Common::String text;
+ reader.readString(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..42c1cd7c5d
--- /dev/null
+++ b/engines/sword25/gfx/text.h
@@ -0,0 +1,169 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#include "sword25/kernel/common.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+class Kernel;
+class FontResource;
+class ResourceManager;
+
+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 _text;
+ }
+
+ /**
+ @brief Gibt den Namen das momentan benutzten Fonts zurück.
+ */
+ const Common::String &getFont() {
+ return _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 _autoWrap;
+ }
+
+ /**
+ @brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
+ */
+ uint getAutoWrapThreshold() const {
+ return _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 _font;
+ Common::String _text;
+ bool _autoWrap;
+ uint _autoWrapThreshold;
+
+ struct Line {
+ Common::Rect bbox;
+ Common::String text;
+ };
+
+ Common::Array<Line> _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..6fee19882a
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.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
+ *
+ */
+
+#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..8dc98ba3fb
--- /dev/null
+++ b/engines/sword25/input/inputengine.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
+ *
+ */
+
+#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/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),
+ _currentState(0),
+ _leftMouseDown(false),
+ _rightMouseDown(false),
+ _mouseX(0),
+ _mouseY(0),
+ _leftDoubleClick(false),
+ _doubleClickTime(DOUBLE_CLICK_TIME),
+ _doubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
+ _doubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
+ _lastLeftClickTime(0),
+ _lastLeftClickMouseX(0),
+ _lastLeftClickMouseY(0) {
+ memset(_keyboardState[0], 0, sizeof(_keyboardState[0]));
+ memset(_keyboardState[1], 0, sizeof(_keyboardState[1]));
+ _leftMouseState[0] = false;
+ _leftMouseState[1] = false;
+ _rightMouseState[0] = false;
+ _rightMouseState[1] = false;
+
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+InputEngine::~InputEngine() {
+ unregisterScriptBindings();
+}
+
+bool InputEngine::init() {
+ // No initialisation needed
+ return true;
+}
+
+void InputEngine::update() {
+ Common::Event event;
+
+ // We keep two sets of keyboard states: The current one, and that of
+ // the previous frame. This allows us to detect which keys changed
+ // state. Also, by keeping a single central keystate array, we
+ // ensure that all script queries for key state during a single
+ // frame get the same consistent replies.
+ _currentState ^= 1;
+ memcpy(_keyboardState[_currentState], _keyboardState[_currentState ^ 1], sizeof(_keyboardState[0]));
+
+ // 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:
+ _leftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ handleEvents = false;
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ _rightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ handleEvents = false;
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _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;
+
+ default:
+ break;
+ }
+ }
+
+ _leftMouseState[_currentState] = _leftMouseDown;
+ _rightMouseState[_currentState] = _rightMouseDown;
+
+ testForLeftDoubleClick();
+}
+
+bool InputEngine::isLeftMouseDown() {
+ return _leftMouseDown;
+}
+
+bool InputEngine::isRightMouseDown() {
+ return _rightMouseDown;
+}
+
+void InputEngine::testForLeftDoubleClick() {
+ _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 - _lastLeftClickTime <= _doubleClickTime &&
+ ABS(_mouseX - _lastLeftClickMouseX) <= _doubleClickRectWidth / 2 &&
+ ABS(_mouseY - _lastLeftClickMouseY) <= _doubleClickRectHeight / 2) {
+ _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
+ _lastLeftClickTime = 0;
+ _lastLeftClickMouseX = 0;
+ _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
+ _lastLeftClickTime = now;
+ _lastLeftClickMouseX = _mouseX;
+ _lastLeftClickMouseY = _mouseY;
+ }
+ }
+}
+
+void InputEngine::alterKeyboardState(int keycode, byte newState) {
+ assert(keycode < ARRAYSIZE(_keyboardState[_currentState]));
+ _keyboardState[_currentState][keycode] = newState;
+}
+
+bool InputEngine::isLeftDoubleClick() {
+ return _leftDoubleClick;
+}
+
+bool InputEngine::wasLeftMouseDown() {
+ return (_leftMouseState[_currentState] == false) && (_leftMouseState[_currentState ^ 1] == true);
+}
+
+bool InputEngine::wasRightMouseDown() {
+ return (_rightMouseState[_currentState] == false) && (_rightMouseState[_currentState ^ 1] == true);
+}
+
+int InputEngine::getMouseX() {
+ return _mouseX;
+}
+
+int InputEngine::getMouseY() {
+ return _mouseY;
+}
+
+bool InputEngine::isKeyDown(uint keyCode) {
+ assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
+ return (_keyboardState[_currentState][keyCode] & 0x80) != 0;
+}
+
+bool InputEngine::wasKeyDown(uint keyCode) {
+ assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
+ return ((_keyboardState[_currentState][keyCode] & 0x80) == 0) &&
+ ((_keyboardState[_currentState ^ 1][keyCode] & 0x80) != 0);
+}
+
+void InputEngine::setMouseX(int posX) {
+ _mouseX = posX;
+ g_system->warpMouse(_mouseX, _mouseY);
+}
+
+void InputEngine::setMouseY(int posY) {
+ _mouseY = posY;
+ g_system->warpMouse(_mouseX, _mouseY);
+}
+
+void InputEngine::setCharacterCallback(CharacterCallback callback) {
+ _characterCallback = callback;
+}
+
+void InputEngine::setCommandCallback(CommandCallback callback) {
+ _commandCallback = callback;
+}
+
+void InputEngine::reportCharacter(byte character) {
+ if (_characterCallback)
+ (*_characterCallback)(character);
+}
+
+void InputEngine::reportCommand(KEY_COMMANDS command) {
+ if (_commandCallback)
+ (*_commandCallback)(command);
+}
+
+bool InputEngine::persist(OutputPersistenceBlock &writer) {
+ // Write out the number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaCommandCB");
+
+ // Write out the number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ writer.write((uint)1);
+ writer.writeString("LuaCharacterCB");
+
+ return true;
+}
+
+bool InputEngine::unpersist(InputPersistenceBlock &reader) {
+ Common::String callbackFunctionName;
+
+ // Read number of command callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ uint commandCallbackCount;
+ reader.read(commandCallbackCount);
+ assert(commandCallbackCount == 1);
+
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaCommandCB");
+
+ // Read number of character callbacks and their names.
+ // Note: We do this only for compatibility with older engines resp.
+ // the original engine.
+ uint characterCallbackCount;
+ reader.read(characterCallbackCount);
+ assert(characterCallbackCount == 1);
+
+ reader.readString(callbackFunctionName);
+ assert(callbackFunctionName == "LuaCharacterCB");
+
+ 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..946d6a8e5e
--- /dev/null
+++ b/engines/sword25/input/inputengine.h
@@ -0,0 +1,322 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#include "common/keyboard.h"
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/kernel/persistable.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
+ // If you add or remove entries of this enum, you must also adjust
+ // the above file.
+ enum KEY_CODES {
+ KEY_BACKSPACE = Common::KEYCODE_BACKSPACE,
+ KEY_TAB = Common::KEYCODE_TAB,
+ KEY_CLEAR = Common::KEYCODE_CLEAR,
+ KEY_RETURN = Common::KEYCODE_RETURN,
+ KEY_PAUSE = Common::KEYCODE_PAUSE,
+ KEY_CAPSLOCK = Common::KEYCODE_CAPSLOCK,
+ KEY_ESCAPE = Common::KEYCODE_ESCAPE,
+ KEY_SPACE = Common::KEYCODE_SPACE,
+ KEY_PAGEUP = Common::KEYCODE_PAGEUP,
+ KEY_PAGEDOWN = Common::KEYCODE_PAGEDOWN,
+ KEY_END = Common::KEYCODE_END,
+ KEY_HOME = Common::KEYCODE_HOME,
+ KEY_LEFT = Common::KEYCODE_LEFT,
+ KEY_UP = Common::KEYCODE_UP,
+ KEY_RIGHT = Common::KEYCODE_RIGHT,
+ KEY_DOWN = Common::KEYCODE_DOWN,
+ KEY_PRINTSCREEN = Common::KEYCODE_PRINT,
+ KEY_INSERT = Common::KEYCODE_INSERT,
+ KEY_DELETE = Common::KEYCODE_DELETE,
+ KEY_0 = Common::KEYCODE_0,
+ KEY_1 = Common::KEYCODE_1,
+ KEY_2 = Common::KEYCODE_2,
+ KEY_3 = Common::KEYCODE_3,
+ KEY_4 = Common::KEYCODE_4,
+ KEY_5 = Common::KEYCODE_5,
+ KEY_6 = Common::KEYCODE_6,
+ KEY_7 = Common::KEYCODE_7,
+ KEY_8 = Common::KEYCODE_8,
+ KEY_9 = Common::KEYCODE_9,
+ KEY_A = Common::KEYCODE_a,
+ KEY_B = Common::KEYCODE_b,
+ KEY_C = Common::KEYCODE_c,
+ KEY_D = Common::KEYCODE_d,
+ KEY_E = Common::KEYCODE_e,
+ KEY_F = Common::KEYCODE_f,
+ KEY_G = Common::KEYCODE_g,
+ KEY_H = Common::KEYCODE_h,
+ KEY_I = Common::KEYCODE_i,
+ KEY_J = Common::KEYCODE_j,
+ KEY_K = Common::KEYCODE_k,
+ KEY_L = Common::KEYCODE_l,
+ KEY_M = Common::KEYCODE_m,
+ KEY_N = Common::KEYCODE_n,
+ KEY_O = Common::KEYCODE_o,
+ KEY_P = Common::KEYCODE_p,
+ KEY_Q = Common::KEYCODE_q,
+ KEY_R = Common::KEYCODE_r,
+ KEY_S = Common::KEYCODE_s,
+ KEY_T = Common::KEYCODE_t,
+ KEY_U = Common::KEYCODE_u,
+ KEY_V = Common::KEYCODE_v,
+ KEY_W = Common::KEYCODE_w,
+ KEY_X = Common::KEYCODE_x,
+ KEY_Y = Common::KEYCODE_y,
+ KEY_Z = Common::KEYCODE_z,
+ KEY_NUMPAD0 = Common::KEYCODE_KP0,
+ KEY_NUMPAD1 = Common::KEYCODE_KP1,
+ KEY_NUMPAD2 = Common::KEYCODE_KP2,
+ KEY_NUMPAD3 = Common::KEYCODE_KP3,
+ KEY_NUMPAD4 = Common::KEYCODE_KP4,
+ KEY_NUMPAD5 = Common::KEYCODE_KP5,
+ KEY_NUMPAD6 = Common::KEYCODE_KP6,
+ KEY_NUMPAD7 = Common::KEYCODE_KP7,
+ KEY_NUMPAD8 = Common::KEYCODE_KP8,
+ KEY_NUMPAD9 = Common::KEYCODE_KP9,
+ KEY_MULTIPLY = Common::KEYCODE_KP_MULTIPLY,
+ KEY_ADD = Common::KEYCODE_KP_PLUS,
+ KEY_SEPARATOR = Common::KEYCODE_EQUALS, // FIXME: This mapping is just a wild guess!!
+ KEY_SUBTRACT = Common::KEYCODE_KP_MINUS,
+ KEY_DECIMAL = Common::KEYCODE_KP_PERIOD,
+ KEY_DIVIDE = Common::KEYCODE_KP_DIVIDE,
+ KEY_F1 = Common::KEYCODE_F1,
+ KEY_F2 = Common::KEYCODE_F2,
+ KEY_F3 = Common::KEYCODE_F3,
+ KEY_F4 = Common::KEYCODE_F4,
+ KEY_F5 = Common::KEYCODE_F5,
+ KEY_F6 = Common::KEYCODE_F6,
+ KEY_F7 = Common::KEYCODE_F7,
+ KEY_F8 = Common::KEYCODE_F8,
+ KEY_F9 = Common::KEYCODE_F9,
+ KEY_F10 = Common::KEYCODE_F10,
+ KEY_F11 = Common::KEYCODE_F11,
+ KEY_F12 = Common::KEYCODE_F12,
+ KEY_NUMLOCK = Common::KEYCODE_NUMLOCK,
+ KEY_SCROLL = Common::KEYCODE_SCROLLOCK,
+ KEY_LSHIFT = Common::KEYCODE_LSHIFT,
+ KEY_RSHIFT = Common::KEYCODE_RSHIFT,
+ KEY_LCONTROL = Common::KEYCODE_LCTRL,
+ KEY_RCONTROL = Common::KEYCODE_RCTRL
+ };
+
+ // NOTE: These codes are registered in inputengine_script.cpp
+ // If you add or remove entries of this enum, you 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 void (*CharacterCallback)(int command);
+
+ /**
+ * Registers a callback function for keyboard input.
+ *
+ * The callback that is 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.
+ */
+ void setCharacterCallback(CharacterCallback callback);
+
+ typedef void (*CommandCallback)(int command);
+
+ /**
+ * Registers a callback function for the input of commands that can have influence on the string input
+ *
+ * The callback that is 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.
+ */
+ void setCommandCallback(CommandCallback callback);
+
+ void reportCharacter(byte character);
+ void reportCommand(KEY_COMMANDS command);
+
+ bool persist(OutputPersistenceBlock &writer);
+ bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool registerScriptBindings();
+ void unregisterScriptBindings();
+
+private:
+ void testForLeftDoubleClick();
+ void alterKeyboardState(int keycode, byte newState);
+
+ byte _keyboardState[2][512];
+ bool _leftMouseState[2];
+ bool _rightMouseState[2];
+ uint _currentState;
+ int _mouseX;
+ int _mouseY;
+ bool _leftMouseDown;
+ bool _rightMouseDown;
+ bool _leftDoubleClick;
+ uint _doubleClickTime;
+ int _doubleClickRectWidth;
+ int _doubleClickRectHeight;
+ uint _lastLeftClickTime;
+ int _lastLeftClickMouseX;
+ int _lastLeftClickMouseY;
+ CommandCallback _commandCallback;
+ CharacterCallback _characterCallback;
+};
+
+} // 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..2f16a21377
--- /dev/null
+++ b/engines/sword25/input/inputengine_script.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/ptr.h"
+#include "common/str.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.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 {
+
+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;
+ }
+};
+
+static CharacterCallbackClass *characterCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
+
+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;
+ }
+};
+
+static CommandCallbackClass *commandCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
+
+}
+
+static InputEngine *getIE() {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ InputEngine *pIE = pKernel->getInput();
+ 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();
+
+ 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 = pKernel->getScript();
+ 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;
+
+ assert(characterCallbackPtr == 0);
+ characterCallbackPtr = new CharacterCallbackClass(L);
+
+ assert(commandCallbackPtr == 0);
+ commandCallbackPtr = new CommandCallbackClass(L);
+
+ setCharacterCallback(theCharacterCallback);
+ setCommandCallback(theCommandCallback);
+
+ return true;
+}
+
+void InputEngine::unregisterScriptBindings() {
+ delete characterCallbackPtr;
+ characterCallbackPtr = 0;
+
+ delete commandCallbackPtr;
+ commandCallbackPtr = 0;
+}
+
+} // End of namespace Sword25
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..d05ac922e1
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil.cpp
@@ -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
+ *
+ */
+
+#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"
+
+Common::String FileSystemUtil::getUserdataDirectory() {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
+ Common::String path = ConfMan.get("savepath");
+
+ if (path.empty()) {
+ error("No save path has been defined");
+ return "";
+ }
+
+ // Return the path
+ return path;
+}
+
+Common::String FileSystemUtil::getPathSeparator() {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
+ return Common::String("/");
+}
+
+bool FileSystemUtil::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(getPathFilename(filename));
+ return fileNode.exists();
+}
+
+Common::String FileSystemUtil::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;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/filesystemutil.h b/engines/sword25/kernel/filesystemutil.h
new file mode 100644
index 0000000000..38a3fdaa12
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil.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$
+ *
+ */
+
+/*
+ * 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::instance()
+ */
+
+#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"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class definitions
+// -----------------------------------------------------------------------------
+
+class FileSystemUtil {
+public:
+
+ /**
+ * 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.
+ */
+ static Common::String getUserdataDirectory();
+
+ /**
+ * @return Returns the path seperator
+ */
+ static Common::String getPathSeparator();
+
+ /**
+ * @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
+ */
+ static int32 getFileSize(const Common::String &filename);
+
+ /**
+ * @param Filename The path to a file.
+ * @return Returns true if the file exists.
+ */
+ static bool fileExists(const Common::String &filename);
+
+ /**
+ * Gets the filename from a path and filename
+ * @param Filename The full path and filename
+ * @return Returns just the filename
+ */
+ static Common::String getPathFilename(const Common::String &path);
+};
+
+} // 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..652c2f362f
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.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
+ *
+ */
+
+#define BS_LOG_PREFIX "INPUTPERSISTENCEBLOCK"
+
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+InputPersistenceBlock::InputPersistenceBlock(const void *data, uint dataLength) :
+ _data(static_cast<const byte *>(data), dataLength),
+ _errorState(NONE) {
+ _iter = _data.begin();
+}
+
+InputPersistenceBlock::~InputPersistenceBlock() {
+ if (_iter != _data.end())
+ BS_LOG_WARNINGLN("Persistence block was not read to the end.");
+}
+
+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::readString(Common::String &value) {
+ value = "";
+
+ if (checkMarker(STRING_MARKER)) {
+ uint size;
+ read(size);
+
+ if (checkBlockSize(size)) {
+ value = Common::String(reinterpret_cast<const char *>(&*_iter), size);
+ _iter += size;
+ }
+ }
+}
+
+void InputPersistenceBlock::readByteArray(Common::Array<byte> &value) {
+ if (checkMarker(BLOCK_MARKER)) {
+ uint size;
+ read(size);
+
+ if (checkBlockSize(size)) {
+ value = Common::Array<byte>(_iter, size);
+ _iter += size;
+ }
+ }
+}
+
+void InputPersistenceBlock::rawRead(void *destPtr, size_t size) {
+ if (checkBlockSize(size)) {
+ memcpy(destPtr, &*_iter, size);
+ _iter += size;
+ }
+}
+
+bool InputPersistenceBlock::checkBlockSize(int size) {
+ if (_data.end() - _iter >= size) {
+ return true;
+ } else {
+ _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 (*_iter++ == marker) {
+ return true;
+ } else {
+ _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..f6ab256460
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.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$
+ *
+ */
+
+/*
+ * 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
+
+#include "common/array.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistenceblock.h"
+
+namespace Sword25 {
+
+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 readString(Common::String &value);
+ void readByteArray(Common::Array<byte> &value);
+
+ bool isGood() const {
+ return _errorState == NONE;
+ }
+ ErrorState getErrorState() const {
+ return _errorState;
+ }
+
+private:
+ bool checkMarker(byte marker);
+ bool checkBlockSize(int size);
+ void rawRead(void *destPtr, size_t size);
+
+ Common::Array<byte> _data;
+ Common::Array<byte>::const_iterator _iter;
+ ErrorState _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..9bd8ac5ff7
--- /dev/null
+++ b/engines/sword25/kernel/kernel.cpp
@@ -0,0 +1,211 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/math/geometry.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/script/luascript.h"
+#include "sword25/sfx/soundengine.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "KERNEL"
+
+Kernel *Kernel::_instance = 0;
+
+Kernel::Kernel() :
+ _resourceManager(NULL),
+ _initSuccess(false),
+ _gfx(0),
+ _sfx(0),
+ _input(0),
+ _package(0),
+ _script(0),
+ _fmv(0)
+ {
+
+ // Log that the kernel is beign created
+ BS_LOGLN("created.");
+ _instance = this;
+
+ // Create the resource manager
+ _resourceManager = new ResourceManager(this);
+
+ // Initialise the script engine
+ _script = new LuaScriptEngine(this);
+ if (!_script || !_script->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.");
+
+ _input = new InputEngine(this);
+ assert(_input);
+
+ _gfx = new GraphicEngine(this);
+ assert(_gfx);
+
+ _sfx = new SoundEngine(this);
+ assert(_sfx);
+
+ _package = new PackageManager(this);
+ assert(_package);
+
+ _geometry = new Geometry(this);
+ assert(_geometry);
+
+#ifdef USE_THEORADEC
+ _fmv = new MoviePlayer(this);
+ assert(_fmv);
+#endif
+
+ _initSuccess = true;
+}
+
+Kernel::~Kernel() {
+ // Services are de-registered in reverse order of creation
+
+ delete _input;
+ _input = 0;
+
+ delete _gfx;
+ _gfx = 0;
+
+ delete _sfx;
+ _sfx = 0;
+
+ delete _package;
+ _package = 0;
+
+ delete _geometry;
+ _geometry = 0;
+
+#ifdef USE_THEORADEC
+ delete _fmv;
+ _fmv = 0;
+#endif
+
+ delete _script;
+ _script = 0;
+
+ // Resource-Manager freigeben
+ delete _resourceManager;
+
+ BS_LOGLN("destroyed.");
+}
+
+/**
+ * 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 how much memory is being used
+ */
+size_t Kernel::getUsedMemory() {
+ return 0;
+}
+
+/**
+ * Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active.
+ */
+GraphicEngine *Kernel::getGfx() {
+ return _gfx;
+}
+
+/**
+ * Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active.
+ */
+SoundEngine *Kernel::getSfx() {
+ return _sfx;
+}
+
+/**
+ * Returns a pointer to the active input service, or NULL if no input service is active.
+ */
+InputEngine *Kernel::getInput() {
+ return _input;
+}
+
+/**
+ * Returns a pointer to the active package manager, or NULL if no manager is active.
+ */
+PackageManager *Kernel::getPackage() {
+ return _package;
+}
+
+/**
+ * Returns a pointer to the script engine, or NULL if it is not active.
+ */
+ScriptEngine *Kernel::getScript() {
+ return _script;
+}
+
+/**
+ * Returns a pointer to the movie player, or NULL if it is not active.
+ */
+MoviePlayer *Kernel::getFMV() {
+ return _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..252de64e80
--- /dev/null
+++ b/engines/sword25/kernel/kernel.h
@@ -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$
+ *
+ */
+
+/*
+ * 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
+
+#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/resmanager.h"
+
+namespace Sword25 {
+
+// Class definitions
+class Service;
+class Geometry;
+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:
+
+ /**
+ * Returns the elapsed time since startup in milliseconds
+ */
+ uint getMilliTicks();
+
+ /**
+ * Specifies whether the kernel was successfully initialised
+ */
+ bool getInitSuccess() const {
+ return _initSuccess;
+ }
+ /**
+ * Returns a pointer to the BS_ResourceManager
+ */
+ ResourceManager *getResourceManager() {
+ return _resourceManager;
+ }
+ /**
+ * 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("Kernel::Crash");
+ }
+
+private:
+ // -----------------------------------------------------------------------------
+ // Constructor / destructor
+ // Private singleton methods
+ // -----------------------------------------------------------------------------
+
+ Kernel();
+ virtual ~Kernel();
+
+ // -----------------------------------------------------------------------------
+ // Singleton instance
+ // -----------------------------------------------------------------------------
+ static Kernel *_instance;
+
+ bool _initSuccess; // Specifies whether the engine was set up correctly
+
+ // Random number generator
+ // -----------------------
+ Common::RandomSource _rnd;
+
+ // Resourcemanager
+ // ---------------
+ ResourceManager *_resourceManager;
+
+ GraphicEngine *_gfx;
+ SoundEngine *_sfx;
+ InputEngine *_input;
+ PackageManager *_package;
+ ScriptEngine *_script;
+ Geometry *_geometry;
+ MoviePlayer *_fmv;
+
+ bool registerScriptBindings();
+};
+
+} // 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..d4b9a56d8e
--- /dev/null
+++ b/engines/sword25/kernel/kernel_script.cpp
@@ -0,0 +1,550 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/kernel/filesystemutil.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) {
+ // This function apparently is not used by the game scripts
+ lua_pushboolean(L, true);
+
+ return 1;
+}
+
+static int getActiveServiceIdentifier(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushstring(L, "QUUX");
+
+ return 1;
+}
+
+static int getSuperclassCount(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getSuperclassIdentifier(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushstring(L, "FOO");
+
+ return 1;
+}
+
+static int getServiceCount(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getServiceIdentifier(lua_State *L) {
+ // This function is only used by a single function in system/kernel.lua which is never called.
+ lua_pushstring(L, "BAR");
+
+ 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->getMilliTicks()) / 1000.0);
+
+ return 1;
+}
+
+static int startService(lua_State *L) {
+ // This function is used by system/boot.lua to init all services.
+ // However, we do nothing here, as we just hard code the init sequence.
+ lua_pushbooleancpp(L, true);
+
+ 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 = pKernel->getScript();
+ BS_ASSERT(pSE);
+
+ lua_pushbooleancpp(L, pSE->executeFile(luaL_checkstring(L, 1)));
+
+ return 0;
+}
+
+static int getUserdataDirectory(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::getUserdataDirectory().c_str());
+ return 1;
+}
+
+static int getPathSeparator(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::getPathSeparator().c_str());
+ return 1;
+}
+
+static int fileExists(lua_State *L) {
+ lua_pushbooleancpp(L, FileSystemUtil::fileExists(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+static int createDirectory(lua_State *L) {
+ // ScummVM engines cannot create directories, so we do nothing here.
+ lua_pushbooleancpp(L, false);
+ 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) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
+
+ return 1;
+}
+
+static int setVisible(lua_State *L) {
+ // This function apparently is not used by the game scripts
+// pWindow->setVisible(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+static int getX(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getY(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int setX(lua_State *L) {
+ // This is called by system/boot.lua with -1 as value.
+// pWindow->setX(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int setY(lua_State *L) {
+ // This is called by system/boot.lua with -1 as value.
+// pWindow->setY(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int getClientX(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getClientY(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getWidth(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 800);
+
+ return 1;
+}
+
+static int getHeight(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 600);
+
+ return 1;
+}
+
+static int setWidth(lua_State *L) {
+ // This is called by system/boot.lua with 800 as value.
+// pWindow->setWidth(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int setHeight(lua_State *L) {
+ // This is called by system/boot.lua with 600 as value.
+// pWindow->setHeight(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int getTitle(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushstring(L, "");
+
+ return 1;
+}
+
+static int setTitle(lua_State *L) {
+ // This is called by system/boot.lua and system/menu.lua, to
+ // set the window title to the (localized) game name.
+ // FIXME: Should we call OSystem::setWindowCaption() here?
+// pWindow->setTitle(luaL_checkstring(L, 1));
+
+ return 0;
+}
+
+static int processMessages(lua_State *L) {
+ // This is called by the main loop in system/boot.lua,
+ // and the game keeps running as true is returned here.
+ // It terminates if we return false.
+
+ // TODO: We could do more stuff here if desired...
+
+ // TODO: We could always return true here, and leave quit handling
+ // to the closeWanted() opcode; see also the TODO comment in there.
+
+ lua_pushbooleancpp(L, !Engine::shouldQuit());
+
+ return 1;
+}
+
+static int closeWanted(lua_State *L) {
+ // This is called by system/interface.lua to determine whether the
+ // user requested the game to close (e.g. by clicking the 'close' widget
+ // of the game window). As a consequence (i.e. this function returns true),
+ // a quit confirmation dialog is shown.
+
+ // TODO: ScummVM currently has a bug / misfeature where some engines provide
+ // quit confirmation dialogs, some don't; in addition, we have a global confirmation
+ // dialog (but the user has to explicitly activate that in the config).
+ // Anyway, this can lead to *two* confirmation dialogs being shown.
+ // If it wasn't for that, we could simply check for Engine::shouldQuit() here,
+ // and then invoke EventMan::resetQuit. But currently this would result in
+ // the user seeing two confirmation dialogs. Bad.
+ lua_pushbooleancpp(L, false);
+
+ return 1;
+}
+
+static int waitForFocus(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
+
+ return 1;
+}
+
+static int hasFocus(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushbooleancpp(L, true);
+
+ 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 = getScript();
+ 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..91b0c36ac1
--- /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 {
+
+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;
+
+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: ", 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) {
+ Common::strlcat(extFormat, format, sizeof(extFormat));
+ if (nextLine)
+ _lineBegin = true;
+ break;
+ } else {
+ strncat(extFormat, format, (nextLine - format) + strlen("\n"));
+ Common::strlcat(extFormat, prefix, sizeof(extFormat));
+ Common::strlcat(extFormat, ": ", sizeof(extFormat));
+ }
+
+ 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 && !createLog())
+ return false;
+
+ debugN(0, "%s", message);
+
+ _logFile->writeString(message);
+
+ return true;
+}
+
+void BS_Log::flushLog() {
+ if (_logFile)
+ _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..e7c5789a53
--- /dev/null
+++ b/engines/sword25/kernel/log.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 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;
+ }
+
+ 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 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..3a27ba4942
--- /dev/null
+++ b/engines/sword25/kernel/objectregistry.h
@@ -0,0 +1,173 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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:
+ 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 *(uint *)&x;
+ }
+ };
+
+ 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..b1c3233b59
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.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 Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "OUTPUTPERSISTENCEBLOCK"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+
+namespace {
+const uint INITIAL_BUFFER_SIZE = 1024 * 64;
+}
+
+namespace Sword25 {
+
+OutputPersistenceBlock::OutputPersistenceBlock() {
+ _data.reserve(INITIAL_BUFFER_SIZE);
+}
+
+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::writeString(const Common::String &string) {
+ writeMarker(STRING_MARKER);
+
+ write(string.size());
+ rawWrite(string.c_str(), string.size());
+}
+
+void OutputPersistenceBlock::writeByteArray(Common::Array<byte> &value) {
+ writeMarker(BLOCK_MARKER);
+
+ write((uint)value.size());
+ rawWrite(&value[0], value.size());
+}
+
+void OutputPersistenceBlock::writeMarker(byte marker) {
+ _data.push_back(marker);
+}
+
+void OutputPersistenceBlock::rawWrite(const void *dataPtr, size_t size) {
+ if (size > 0) {
+ uint oldSize = _data.size();
+ _data.resize(oldSize + size);
+ memcpy(&_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..71dbe68a52
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistenceblock.h"
+
+namespace Sword25 {
+
+class OutputPersistenceBlock : public PersistenceBlock {
+public:
+ OutputPersistenceBlock();
+
+ void write(signed int value);
+ void write(uint value);
+ void write(float value);
+ void write(bool value);
+ void writeString(const Common::String &string);
+ void writeByteArray(Common::Array<byte> &value);
+
+ const void *getData() const {
+ return &_data[0];
+ }
+ uint getDataSize() const {
+ return _data.size();
+ }
+
+private:
+ void writeMarker(byte marker);
+ void rawWrite(const void *dataPtr, size_t size);
+
+ Common::Array<byte> _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..25cf70fda0
--- /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..4a64eff11b
--- /dev/null
+++ b/engines/sword25/kernel/persistenceblock.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_PERSISTENCEBLOCK_H
+#define SWORD25_PERSISTENCEBLOCK_H
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+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]);
+ }
+};
+
+#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..564f031cf3
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.cpp
@@ -0,0 +1,420 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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/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"
+
+namespace Sword25 {
+
+//static const char *SAVEGAME_EXTENSION = ".b25s";
+static const char *SAVEGAME_DIRECTORY = "saves";
+static const char *FILE_MARKER = "BS25SAVEGAME";
+static const uint SLOT_COUNT = 18;
+static const uint FILE_COPY_BUFFER_SIZE = 1024 * 10;
+static const char *VERSIONID = "SCUMMVM1";
+
+#define MAX_SAVEGAME_SIZE 100
+
+char gameTarget[MAX_SAVEGAME_SIZE];
+
+void setGameTarget(const char *target) {
+ strncpy(gameTarget, target, MAX_SAVEGAME_SIZE);
+}
+
+static Common::String generateSavegameFilename(uint slotID) {
+ char buffer[MAX_SAVEGAME_SIZE];
+ snprintf(buffer, MAX_SAVEGAME_SIZE, "%s.%.3d", gameTarget, slotID);
+ return Common::String(buffer);
+}
+
+static 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);
+}
+
+static Common::String loadString(Common::InSaveFile *in, uint maxSize = 999) {
+ Common::String result;
+
+ char ch = (char)in->readByte();
+ while (ch != '\0') {
+ result += ch;
+ if (result.size() >= maxSize)
+ break;
+ ch = (char)in->readByte();
+ }
+
+ return result;
+}
+
+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 _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 = _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 gameDescription = 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 = gameDescription;
+ // 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;
+ }
+ }
+};
+
+PersistenceService &PersistenceService::getInstance() {
+ static PersistenceService instance;
+ return instance;
+}
+
+PersistenceService::PersistenceService() : _impl(new Impl) {
+}
+
+PersistenceService::~PersistenceService() {
+ delete _impl;
+}
+
+void PersistenceService::reloadSlots() {
+ _impl->reloadSlots();
+}
+
+uint PersistenceService::getSlotCount() {
+ return SLOT_COUNT;
+}
+
+Common::String PersistenceService::getSavegameDirectory() {
+ Common::FSNode node(FileSystemUtil::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 _impl->_savegameInformations[slotID].isOccupied;
+}
+
+bool PersistenceService::isSavegameCompatible(uint slotID) {
+ if (!checkslotID(slotID))
+ return false;
+ return _impl->_savegameInformations[slotID].isCompatible;
+}
+
+Common::String &PersistenceService::getSavegameDescription(uint slotID) {
+ static Common::String emptyString;
+ if (!checkslotID(slotID))
+ return emptyString;
+ return _impl->_savegameInformations[slotID].description;
+}
+
+Common::String &PersistenceService::getSavegameFilename(uint slotID) {
+ static Common::String emptyString;
+ if (!checkslotID(slotID))
+ return emptyString;
+ return _impl->_savegameInformations[slotID].filename;
+}
+
+bool PersistenceService::saveGame(uint slotID, const Common::String &screenshotFilename) {
+ // FIXME: This code is a hack which bypasses the savefile API,
+ // and should eventually be removed.
+
+ // Ü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);
+
+ // Spielstanddatei öffnen und die Headerdaten schreiben.
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ Common::OutSaveFile *file = sfm->openForSaving(filename);
+
+ file->writeString(FILE_MARKER);
+ file->writeByte(0);
+ file->writeString(VERSIONID);
+ file->writeByte(0);
+
+ TimeDate dt;
+ g_system->getTimeAndDate(dt);
+ file->writeString(formatTimestamp(dt));
+ file->writeByte(0);
+
+ 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::instance().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(0);
+ snprintf(sBuffer, 10, "%u", writer.getDataSize());
+ file->writeString(sBuffer);
+ file->writeByte(0);
+
+ // 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 = Kernel::getInstance()->getGfx()->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());
+ }
+
+ file->finalize();
+ delete file;
+ delete[] compressionBuffer;
+
+ // Savegameinformationen für diesen Slot aktualisieren.
+ _impl->readSlotSavegameInformation(slotID);
+
+ // 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 = _impl->_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::instance().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..0db109d1b0
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.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_PERSISTENCESERVICE_H
+#define SWORD25_PERSISTENCESERVICE_H
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+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 *_impl;
+};
+
+void setGameTarget(const char *target);
+
+} // 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..1979e6e6c6
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.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 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/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 = _resources.begin();
+ for (; iter != _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);
+ }
+}
+
+/**
+ * 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;
+ }
+
+ _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 (_kernelPtr->getUsedMemory() < _maxMemoryUsage || _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 = _resources.end();
+ do {
+ --iter;
+
+ // The resource may be released only if it isn't locked
+ if ((*iter)->getLockCount() == 0)
+ iter = deleteResource(*iter);
+ } while (iter != _resources.begin() && _kernelPtr->getUsedMemory() > _maxMemoryUsage);
+}
+
+/**
+ * Releases all resources that are not locked.
+ */
+void ResourceManager::emptyCache() {
+ // Scan through the resource list
+ Common::List<Resource *>::iterator iter = _resources.begin();
+ while (iter != _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.empty())
+ 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 (_logCacheMiss)
+ BS_LOG_WARNINGLN("\"%s\" was not precached.", uniqueFileName.c_str());
+
+ Resource *pResource = loadResource(uniqueFileName);
+ if (pResource) {
+ 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.empty())
+ 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
+ _resources.erase(pResource->_iterator);
+ // Re-add the resource at the front of the list
+ _resources.push_front(pResource);
+ // Reset the resource iterator to the repositioned item
+ pResource->_iterator = _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 < _resourceServices.size(); ++i) {
+ if (_resourceServices[i]->canLoadResource(fileName)) {
+ // If more memory is desired, memory must be released
+ deleteResourcesIfNecessary();
+
+ // Load the resource
+ Resource *pResource = _resourceServices[i]->loadResource(fileName);
+ if (!pResource) {
+ BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", fileName.c_str());
+ return NULL;
+ }
+
+ // Add the resource to the front of the list
+ _resources.push_front(pResource);
+ pResource->_iterator = _resources.begin();
+
+ // Also store the resource in the hash table for quick lookup
+ _resourceHashMap[pResource->getFileName()] = 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 *)_kernelPtr->getPackage();
+ 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.empty())
+ 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
+ _resourceHashMap.erase(pResource->_fileName);
+
+ // Delete the resource from the resource list
+ Common::List<Resource *>::iterator result = _resources.erase(pResource->_iterator);
+
+ // Delete the resource
+ delete pResource;
+
+ // 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
+ */
+Resource *ResourceManager::getResource(const Common::String &uniquefileName) const {
+ // Determine whether the resource is already loaded
+ ResMap::iterator it = _resourceHashMap.find(uniquefileName);
+ if (it != _resourceHashMap.end())
+ return it->_value;
+
+ // Resource was not found, i.e. has not yet been loaded.
+ return NULL;
+}
+
+/**
+ * Writes the names of all currently locked resources to the log file
+ */
+void ResourceManager::dumpLockedResources() {
+ for (Common::List<Resource *>::iterator iter = _resources.begin(); iter != _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) {
+ _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..613bb3a3a2
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.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_RESOURCEMANAGER_H
+#define SWORD25_RESOURCEMANAGER_H
+
+#include "common/list.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+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);
+
+ /**
+ * 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 _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 _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) {
+ _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) :
+ _kernelPtr(pKernel),
+ _maxMemoryUsage(100000000),
+ _logCacheMiss(false)
+ {}
+ virtual ~ResourceManager();
+
+ /**
+ * 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
+ */
+ Resource *getResource(const Common::String &uniqueFileName) const;
+
+ /**
+ * Deletes resources as necessary until the specified memory limit is not being exceeded.
+ */
+ void deleteResourcesIfNecessary();
+
+ Kernel *_kernelPtr;
+ uint _maxMemoryUsage;
+ Common::Array<ResourceService *> _resourceServices;
+ Common::List<Resource *> _resources;
+ typedef Common::HashMap<Common::String, Resource *> ResMap;
+ ResMap _resourceHashMap;
+ bool _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..40eea2138c
--- /dev/null
+++ b/engines/sword25/kernel/resource.cpp
@@ -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
+ *
+ */
+
+#include "sword25/kernel/resource.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) {
+ PackageManager *pPM = Kernel::getInstance()->getPackage();
+ BS_ASSERT(pPM);
+
+ _fileName = pPM->getAbsolutePath(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..5c6108a281
--- /dev/null
+++ b/engines/sword25/kernel/resource.h
@@ -0,0 +1,110 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 a resource's type
+ */
+ uint getType() const {
+ return _type;
+ }
+
+protected:
+ virtual ~Resource() {}
+
+private:
+ Common::String _fileName; ///< The absolute 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..a0f2669231
--- /dev/null
+++ b/engines/sword25/kernel/resservice.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$
+ *
+ */
+
+/*
+ * 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
+
+#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/service.h b/engines/sword25/kernel/service.h
new file mode 100644
index 0000000000..ef8858bb7d
--- /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/math/geometry.cpp b/engines/sword25/math/geometry.cpp
new file mode 100644
index 0000000000..bad6fcdb06
--- /dev/null
+++ b/engines/sword25/math/geometry.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$
+ *
+ */
+
+/*
+ * 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.");
+}
+
+
+} // 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..8882d5e588
--- /dev/null
+++ b/engines/sword25/math/geometry_script.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$
+ *
+ */
+
+/*
+ * 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/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::instance().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::instance().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::instance().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::instance().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 = Kernel::getInstance()->getGfx();
+ 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::instance().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 = pKernel->getScript();
+ 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..a83a8aa1dd
--- /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) : Persistable(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..1b2a0b191c
--- /dev/null
+++ b/engines/sword25/math/polygon.h
@@ -0,0 +1,267 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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);
+
+ Polygon &operator=(const Polygon &p) {
+ init(p.vertexCount, p.vertices);
+ return *this;
+ }
+
+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..ae9b76d077
--- /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::instance().registerObject(this);
+}
+
+Region::Region(InputPersistenceBlock &reader, uint handle) : _valid(false), _type(RT_REGION) {
+ RegionRegistry::instance().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::instance().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::instance().resolvePtr(regionPtr);
+}
+
+Region::~Region() {
+ RegionRegistry::instance().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..40909aebad
--- /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"
+
+DECLARE_SINGLETON(Sword25::RegionRegistry)
+
+namespace Sword25 {
+
+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/tools/sci/sciunpack.h b/engines/sword25/math/regionregistry.h
index df605ed19b..560d4ae4a9 100644
--- a/tools/sci/sciunpack.h
+++ b/engines/sword25/math/regionregistry.h
@@ -23,46 +23,41 @@
*
*/
-#ifndef SCI_UNPACK_H_
-#define SCI_UNPACK_H_
+/*
+ * 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 <sciresource.h>
+#ifndef SWORD25_REGIONREGISTRY_H
+#define SWORD25_REGIONREGISTRY_H
-#define SORT_METHOD_ALPHA 0
-#define SORT_METHOD_GROUP 1
+#include "common/singleton.h"
-#define DEFAULT_SORTING SORT_METHOD_ALPHA
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/kernel/objectregistry.h"
-extern int vocab_sort; /* Sorting strategy for vocab */
-extern resource_mgr_t *resmgr;
+namespace Sword25 {
-int
-vocab_print(void);
-/* Prints vocab data
-** Parameters: (void)
-** Returns : (int) 0 on success, 1 on failure
-** Controlled by vocab_sort
-*/
+class Region;
-int
-script_dump(void);
-/* Prints all object information
-** Parameters: (void)
-** Returns : (int) 0 on success, 1 on failure
-*/
+class RegionRegistry :
+ public ObjectRegistry<Region>,
+ public Persistable,
+ public Common::Singleton<RegionRegistry> {
+public:
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
-int
-vocab_dump(void);
-/* Prints full vocabulary information
-** Parameters: (void)
-** Returns : (int) 0 on success, 1 on failure
-*/
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+};
-int
-print_classes(void);
-/* Prints full class information
-** Parameters: (void)
-** Returns : (int) 0 on success, 1 otherwise
-*/
+} // 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..d9a709ab49
--- /dev/null
+++ b/engines/sword25/math/vertex.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#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..b923841a0f
--- /dev/null
+++ b/engines/sword25/math/vertex.h
@@ -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
+ *
+ */
+
+/*
+ BS_Vertex
+ ---------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_VERTEX_H
+#define SWORD25_VERTEX_H
+
+// Includes
+#include <math.h>
+#include "sword25/kernel/common.h"
+#include "sword25/util/lua/lua.h"
+
+#if defined(MACOSX) || defined(SOLARIS) || defined(__MINGW32__)
+// Older versions of Mac OS X didn't supply a powf function, so using it
+// will cause a binary incompatibility when trying to run a binary built
+// on a newer OS X release on an olderr one. And Solaris 8 doesn't provide
+// powf, floorf, fabsf etc. at all.
+// Cross-compiled MinGW32 toolchains suffer from a cross-compile bug in
+// libstdc++. math/stubs.o should be empty, but it comes with a symbol for
+// powf, resulting in a linker error because of multiple definitions.
+// Hence we re-define them here. The only potential drawback is that it
+// might be a little bit slower this way.
+#define powf(x,y) ((float)pow(x,y))
+#define floorf(x) ((float)floor(x))
+#define fabsf(x) ((float)fabs(x))
+#define sqrtf(x) ((float)sqrt(x))
+#define atan2f(x,y) ((float)atan2(x,y))
+#endif
+
+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..51818bc9e8
--- /dev/null
+++ b/engines/sword25/math/walkregion.cpp
@@ -0,0 +1,401 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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;
+ }
+ }
+}
+
+template<class T>
+void reverseArray(Common::Array<T> &arr) {
+ const uint size = arr.size();
+ if (size < 2)
+ return;
+
+ for (uint i = 0; i <= (size / 2 - 1); ++i) {
+ SWAP(arr[i], arr[size - i - 1]);
+ }
+}
+
+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..f10c6be5a9
--- /dev/null
+++ b/engines/sword25/module.mk
@@ -0,0 +1,106 @@
+MODULE := engines/sword25
+
+MODULE_OBJS := \
+ detection.o \
+ sword25.o \
+ fmv/movieplayer.o \
+ fmv/movieplayer_script.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/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/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 \
+ 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/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
+
+ifdef USE_THEORADEC
+MODULE_OBJS += \
+ fmv/theora_decoder.o \
+ fmv/yuvtorgba.o
+endif
+
+# 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..4765d26ed4
--- /dev/null
+++ b/engines/sword25/package/packagemanager.cpp
@@ -0,0 +1,276 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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;
+
+}
+
+/**
+ * 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::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);
+}
+
+bool PackageManager::fileExists(const Common::String &fileName) {
+ // FIXME: The current Zip implementation doesn't support getting a folder entry, which is needed for detecting
+ // the English voick pack
+ if (fileName == "/speech/en") {
+ // To get around this, change to detecting one of the files in the folder
+ return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory));
+ }
+
+ 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..03598012a6
--- /dev/null
+++ b/engines/sword25/package/packagemanager.h
@@ -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 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);
+
+ /**
+ * 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..9367ae3071
--- /dev/null
+++ b/engines/sword25/package/packagemanager_script.cpp
@@ -0,0 +1,211 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 {
+
+static PackageManager *getPM() {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ PackageManager *pPM = pKernel->getPackage();
+ 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) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ return 1;
+}
+
+static int getFileType(lua_State *L) {
+ // This function apparently is not used by the game scripts
+ lua_pushnumber(L, 0);
+
+ 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 = pKernel->getScript();
+ 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..25a43e17d2
--- /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..dc45104d53
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.h
@@ -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
+ *
+ */
+
+#ifndef SWORD25_LUABINDHELPER_H
+#define SWORD25_LUABINDHELPER_H
+
+#include "sword25/kernel/common.h"
+
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+
+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..bb2c821aa8
--- /dev/null
+++ b/engines/sword25/script/luacallback.cpp
@@ -0,0 +1,173 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#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..e097f5b499
--- /dev/null
+++ b/engines/sword25/script/luacallback.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_LUACALLBACK_H
+#define SWORD25_LUACALLBACK_H
+
+#include "sword25/kernel/common.h"
+
+struct lua_State;
+
+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..aa2bdb9e06
--- /dev/null
+++ b/engines/sword25/script/luascript.cpp
@@ -0,0 +1,557 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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"
+
+#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 {
+
+LuaScriptEngine::LuaScriptEngine(Kernel *KernelPtr) :
+ ScriptEngine(KernelPtr),
+ _state(0),
+ _pcallErrorhandlerRegistryIndex(0) {
+}
+
+LuaScriptEngine::~LuaScriptEngine() {
+ // Lua de-initialisation
+ if (_state)
+ lua_close(_state);
+}
+
+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 = Kernel::getInstance()->getPackage();
+ 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.writeByteArray(chunkData);
+
+ // 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.readByteArray(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..b66c32176a
--- /dev/null
+++ b/engines/sword25/script/luascript.h
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * 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"
+#include "sword25/util/lua/lua.h"
+
+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..9ca146026e
--- /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..fa39639f51
--- /dev/null
+++ b/engines/sword25/sfx/soundengine.cpp
@@ -0,0 +1,270 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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;
+}
+
+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..b8c10cc293
--- /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..eabbef6e5e
--- /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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ BS_ASSERT(pSfx);
+
+ pSfx->update();
+
+ return 0;
+}
+
+static int setVolume(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ BS_ASSERT(pSfx);
+
+ pSfx->pauseAll();
+
+ return 0;
+}
+
+static int resumeAll(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = pKernel->getSfx();
+ BS_ASSERT(pSfx);
+
+ pSfx->resumeAll();
+
+ return 0;
+}
+
+static int pauseLayer(lua_State *L) {
+ Kernel *pKernel = Kernel::getInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getSfx();
+ 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 = pKernel->getScript();
+ 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..5864057423
--- /dev/null
+++ b/engines/sword25/sword25.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$
+ *
+ */
+
+/*
+ * 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/kernel/persistenceservice.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/script/script.h"
+
+#include "sword25/gfx/animationtemplateregistry.h" // Needed so we can destroy the singleton
+#include "sword25/gfx/renderobjectregistry.h" // Needed so we can destroy the singleton
+#include "sword25/math/regionregistry.h" // Needed so we can destroy the singleton
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "MAIN"
+
+const char *const PACKAGE_MANAGER = "archiveFS";
+const char *const DEFAULT_SCRIPT_FILE = "/system/boot.lua";
+
+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() {
+ // 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;
+ }
+
+ // Load packages
+ PackageManager *packageManagerPtr = Kernel::getInstance()->getPackage();
+ if (getGameFlags() & GF_EXTRACTED) {
+ if (!packageManagerPtr->loadDirectoryAsPackage(ConfMan.get("path"), "/"))
+ return Common::kUnknownError;
+ } else {
+ if (!loadPackages())
+ return Common::kUnknownError;
+ }
+
+ // Pass the command line to the script engine.
+ ScriptEngine *scriptPtr = Kernel::getInstance()->getScript();
+ if (!scriptPtr) {
+ BS_LOG_ERRORLN("Script intialization failed.");
+ return Common::kUnknownError;
+ }
+
+ // Set the game target for use in savegames
+ setGameTarget(_targetName.c_str());
+
+ 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 = Kernel::getInstance()->getScript();
+ BS_ASSERT(scriptPtr);
+ scriptPtr->executeFile(DEFAULT_SCRIPT_FILE);
+
+ return true;
+}
+
+bool Sword25Engine::appEnd() {
+ // The kernel is shutdown, and un-initialises all subsystems
+ Kernel::deleteInstance();
+
+ AnimationTemplateRegistry::destroy();
+ RenderObjectRegistry::destroy();
+ RegionRegistry::destroy();
+
+ // Free the log file if it was used
+ BS_Log::closeLog();
+
+ return true;
+}
+
+bool Sword25Engine::loadPackages() {
+ PackageManager *packageManagerPtr = Kernel::getInstance()->getPackage();
+ 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());
+
+ // Identify 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;
+ }
+
+ // Identify 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;
+}
+
+bool Sword25Engine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL);
+ // TODO: Implement more of these features?!
+#if 0
+ return
+ (f == kSupportsSubtitleOptions) ||
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+#endif
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/sword25.h b/engines/sword25/sword25.h
new file mode 100644
index 0000000000..2d93e2267c
--- /dev/null
+++ b/engines/sword25/sword25.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$
+ *
+ */
+
+#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;
+
+/**
+ * This is the namespace of the Sword25 engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Broken Sword 2.5
+ */
+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();
+ bool hasFeature(EngineFeature f) const;
+// void pauseEngineIntern(bool pause); // TODO: Implement this!!!
+// void syncSoundSettings(); // TODO: Implement this!!!
+// Common::Error loadGameState(int slot); // TODO: Implement this?
+// Common::Error saveGameState(int slot, const char *desc); // TODO: Implement this?
+// bool canLoadGameStateCurrently(); // TODO: Implement this?
+// bool canSaveGameStateCurrently(); // TODO: Implement this?
+
+ 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.cpp b/engines/sword25/util/lua/lapi.cpp
new file mode 100644
index 0000000000..a38733e1f7
--- /dev/null
+++ b/engines/sword25/util/lua/lapi.cpp
@@ -0,0 +1,1087 @@
+/*
+** $Id$
+** 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);
+ // FIXME: Get rid of const_cast
+ if (o >= L->top) return const_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]
+ // FIXME: Get rid of const_cast
+ : const_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..f968ffc992
--- /dev/null
+++ b/engines/sword25/util/lua/lapi.h
@@ -0,0 +1,16 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lauxlib.cpp
new file mode 100644
index 0000000000..53c0556625
--- /dev/null
+++ b/engines/sword25/util/lua/lauxlib.cpp
@@ -0,0 +1,652 @@
+/*
+** $Id$
+** 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..d58f290527
--- /dev/null
+++ b/engines/sword25/util/lua/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lbaselib.cpp
new file mode 100644
index 0000000000..5032e6322a
--- /dev/null
+++ b/engines/sword25/util/lua/lbaselib.cpp
@@ -0,0 +1,654 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lcode.cpp
new file mode 100644
index 0000000000..6e7e10017f
--- /dev/null
+++ b/engines/sword25/util/lua/lcode.cpp
@@ -0,0 +1,839 @@
+/*
+** $Id$
+** 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..751b2b5695
--- /dev/null
+++ b/engines/sword25/util/lua/lcode.h
@@ -0,0 +1,76 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ldblib.cpp
new file mode 100644
index 0000000000..b2e249e9b7
--- /dev/null
+++ b/engines/sword25/util/lua/ldblib.cpp
@@ -0,0 +1,397 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ldebug.cpp
new file mode 100644
index 0000000000..0b26522b31
--- /dev/null
+++ b/engines/sword25/util/lua/ldebug.cpp
@@ -0,0 +1,622 @@
+/*
+** $Id$
+** 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..22226b4096
--- /dev/null
+++ b/engines/sword25/util/lua/ldebug.h
@@ -0,0 +1,33 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ldo.cpp
new file mode 100644
index 0000000000..07508fbb14
--- /dev/null
+++ b/engines/sword25/util/lua/ldo.cpp
@@ -0,0 +1,518 @@
+/*
+** $Id$
+** 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..4c97134805
--- /dev/null
+++ b/engines/sword25/util/lua/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ldump.cpp
new file mode 100644
index 0000000000..3ce16542d6
--- /dev/null
+++ b/engines/sword25/util/lua/ldump.cpp
@@ -0,0 +1,164 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lfunc.cpp
new file mode 100644
index 0000000000..ce7acf4e77
--- /dev/null
+++ b/engines/sword25/util/lua/lfunc.cpp
@@ -0,0 +1,174 @@
+/*
+** $Id$
+** 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..4c2b7fd138
--- /dev/null
+++ b/engines/sword25/util/lua/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lgc.cpp
new file mode 100644
index 0000000000..52ff72bdc9
--- /dev/null
+++ b/engines/sword25/util/lua/lgc.cpp
@@ -0,0 +1,711 @@
+/*
+** $Id$
+** 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..5123ccb479
--- /dev/null
+++ b/engines/sword25/util/lua/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/linit.cpp
new file mode 100644
index 0000000000..93f41d0350
--- /dev/null
+++ b/engines/sword25/util/lua/linit.cpp
@@ -0,0 +1,38 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/liolib.cpp
new file mode 100644
index 0000000000..aa44dcafa3
--- /dev/null
+++ b/engines/sword25/util/lua/liolib.cpp
@@ -0,0 +1,553 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/llex.cpp
new file mode 100644
index 0000000000..fdde2b8e5f
--- /dev/null
+++ b/engines/sword25/util/lua/llex.cpp
@@ -0,0 +1,461 @@
+/*
+** $Id$
+** 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..fa8b7a2a28
--- /dev/null
+++ b/engines/sword25/util/lua/llex.h
@@ -0,0 +1,81 @@
+/*
+** $Id$
+** 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..a31ad160ad
--- /dev/null
+++ b/engines/sword25/util/lua/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lmathlib.cpp
new file mode 100644
index 0000000000..ed50539f1f
--- /dev/null
+++ b/engines/sword25/util/lua/lmathlib.cpp
@@ -0,0 +1,273 @@
+/*
+** $Id$
+** 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");
+#if defined(MACOSX) && defined(__GNUC__) && ! defined( __XLC__ )
+ // WORKAROUND for a bug in the Mac OS X 10.2.8 SDK. It defines
+ // HUGE_VAL simply to 1e500, leading to this compiler error:
+ // error: floating constant exceeds range of 'double'
+ // However, GCC (at least the version we are using for our cross
+ // compiler) has a __builtin_huge_val which returns the correct
+ // value, so we just use that.
+ lua_pushnumber(L, __builtin_huge_val());
+#else
+ lua_pushnumber(L, HUGE_VAL);
+#endif
+ 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.cpp b/engines/sword25/util/lua/lmem.cpp
new file mode 100644
index 0000000000..ccd69357e0
--- /dev/null
+++ b/engines/sword25/util/lua/lmem.cpp
@@ -0,0 +1,86 @@
+/*
+** $Id$
+** 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..97a888c7f8
--- /dev/null
+++ b/engines/sword25/util/lua/lmem.h
@@ -0,0 +1,49 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/loadlib.cpp
new file mode 100644
index 0000000000..e060611450
--- /dev/null
+++ b/engines/sword25/util/lua/loadlib.cpp
@@ -0,0 +1,664 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lobject.cpp
new file mode 100644
index 0000000000..24718931ed
--- /dev/null
+++ b/engines/sword25/util/lua/lobject.cpp
@@ -0,0 +1,214 @@
+/*
+** $Id$
+** 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..35aaed028a
--- /dev/null
+++ b/engines/sword25/util/lua/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lopcodes.cpp
new file mode 100644
index 0000000000..d9da16f689
--- /dev/null
+++ b/engines/sword25/util/lua/lopcodes.cpp
@@ -0,0 +1,102 @@
+/*
+** $Id$
+** 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..e1aed0f637
--- /dev/null
+++ b/engines/sword25/util/lua/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/loslib.cpp
new file mode 100644
index 0000000000..70a67bccf7
--- /dev/null
+++ b/engines/sword25/util/lua/loslib.cpp
@@ -0,0 +1,243 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lparser.cpp
new file mode 100644
index 0000000000..03ea333315
--- /dev/null
+++ b/engines/sword25/util/lua/lparser.cpp
@@ -0,0 +1,1339 @@
+/*
+** $Id$
+** 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..f9b8e24913
--- /dev/null
+++ b/engines/sword25/util/lua/lparser.h
@@ -0,0 +1,82 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lstate.cpp
new file mode 100644
index 0000000000..495d75c8a6
--- /dev/null
+++ b/engines/sword25/util/lua/lstate.cpp
@@ -0,0 +1,214 @@
+/*
+** $Id$
+** 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..94a6249461
--- /dev/null
+++ b/engines/sword25/util/lua/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lstring.cpp
new file mode 100644
index 0000000000..cd55cc63bf
--- /dev/null
+++ b/engines/sword25/util/lua/lstring.cpp
@@ -0,0 +1,111 @@
+/*
+** $Id$
+** 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..c88e4c12a9
--- /dev/null
+++ b/engines/sword25/util/lua/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lstrlib.cpp
new file mode 100644
index 0000000000..e5501b9b49
--- /dev/null
+++ b/engines/sword25/util/lua/lstrlib.cpp
@@ -0,0 +1,868 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ltable.cpp
new file mode 100644
index 0000000000..b2ec0e912a
--- /dev/null
+++ b/engines/sword25/util/lua/ltable.cpp
@@ -0,0 +1,593 @@
+/*
+** $Id$
+** 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? */
+ // FIXME: Get rid of const_cast
+ t->node = const_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;
+ // FIXME: Get rid of const_cast
+ t->node = const_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)
+ // FIXME: Get rid of const_cast
+ return const_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)
+ // FIXME: Get rid of const_cast
+ return const_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)
+ // FIXME: Get rid of const_cast
+ return const_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..aa28914871
--- /dev/null
+++ b/engines/sword25/util/lua/ltable.h
@@ -0,0 +1,40 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ltablib.cpp
new file mode 100644
index 0000000000..607c09ae71
--- /dev/null
+++ b/engines/sword25/util/lua/ltablib.cpp
@@ -0,0 +1,279 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/ltm.cpp
new file mode 100644
index 0000000000..02856a58fc
--- /dev/null
+++ b/engines/sword25/util/lua/ltm.cpp
@@ -0,0 +1,75 @@
+/*
+** $Id$
+** 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..1b89683ef3
--- /dev/null
+++ b/engines/sword25/util/lua/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id$
+** 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.h b/engines/sword25/util/lua/lua.h
new file mode 100644
index 0000000000..088a511cf9
--- /dev/null
+++ b/engines/sword25/util/lua/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id$
+** 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/luaconf.h b/engines/sword25/util/lua/luaconf.h
new file mode 100644
index 0000000000..fa565c7697
--- /dev/null
+++ b/engines/sword25/util/lua/luaconf.h
@@ -0,0 +1,763 @@
+/*
+** $Id$
+** 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..33d4e314c2
--- /dev/null
+++ b/engines/sword25/util/lua/lualib.h
@@ -0,0 +1,53 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lundump.cpp
new file mode 100644
index 0000000000..4ffc623575
--- /dev/null
+++ b/engines/sword25/util/lua/lundump.cpp
@@ -0,0 +1,225 @@
+/*
+** $Id$
+** 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..f791a4f173
--- /dev/null
+++ b/engines/sword25/util/lua/lundump.h
@@ -0,0 +1,36 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lvm.cpp
new file mode 100644
index 0000000000..ae70fe2645
--- /dev/null
+++ b/engines/sword25/util/lua/lvm.cpp
@@ -0,0 +1,763 @@
+/*
+** $Id$
+** 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..dff2a139f7
--- /dev/null
+++ b/engines/sword25/util/lua/lvm.h
@@ -0,0 +1,36 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/lzio.cpp
new file mode 100644
index 0000000000..e1e7b28a29
--- /dev/null
+++ b/engines/sword25/util/lua/lzio.cpp
@@ -0,0 +1,82 @@
+/*
+** $Id$
+** 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..9aa9e4b537
--- /dev/null
+++ b/engines/sword25/util/lua/lzio.h
@@ -0,0 +1,67 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/lua/print.cpp
new file mode 100644
index 0000000000..22039c9861
--- /dev/null
+++ b/engines/sword25/util/lua/print.cpp
@@ -0,0 +1,227 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/pluto/pdep.cpp
new file mode 100644
index 0000000000..a32c43b42d
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep.cpp
@@ -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..d58f290527
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id$
+** 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..4c97134805
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id$
+** 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..4c2b7fd138
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id$
+** 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..5123ccb479
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id$
+** 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..a31ad160ad
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id$
+** 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..35aaed028a
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id$
+** 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..e1aed0f637
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id$
+** 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..94a6249461
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id$
+** 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..c88e4c12a9
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id$
+** 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..1b89683ef3
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id$
+** 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..68dd887f0f
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id$
+** 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..2f167d7d58
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lzio.h
@@ -0,0 +1,65 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/pluto/pluto.cpp
new file mode 100644
index 0000000000..957f5af795
--- /dev/null
+++ b/engines/sword25/util/pluto/pluto.cpp
@@ -0,0 +1,1665 @@
+/* $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;
+ // FIXME: Casting a pointer to an integer data type is a bad idea we
+ // should really get rid of this by fixing the design of this code.
+ // For now casting to size_t should silence most (all?) compilers,
+ // since size_t is supposedly the same size as a pointer on most
+ // (modern) architectures.
+ int ref = (int)(size_t)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 */
+}
+
+#if 0
+/* 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;
+}
+#endif
+
+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.cpp b/engines/sword25/util/pluto/plzio.cpp
new file mode 100644
index 0000000000..0efc3dfcf2
--- /dev/null
+++ b/engines/sword25/util/pluto/plzio.cpp
@@ -0,0 +1,76 @@
+/*
+** $Id$
+** 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.cpp b/engines/sword25/util/pluto/pptest.cpp
new file mode 100644
index 0000000000..1bfecf2b75
--- /dev/null
+++ b/engines/sword25/util/pluto/pptest.cpp
@@ -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.cpp b/engines/sword25/util/pluto/puptest.cpp
new file mode 100644
index 0000000000..e9aa7ea305
--- /dev/null
+++ b/engines/sword25/util/pluto/puptest.cpp
@@ -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/teenagent/animation.cpp b/engines/teenagent/animation.cpp
index ce1fef009e..56812001e8 100644
--- a/engines/teenagent/animation.cpp
+++ b/engines/teenagent/animation.cpp
@@ -112,7 +112,7 @@ void Animation::load(Common::SeekableReadStream *s, Type type) {
//fixme: do not reload the same animation each time
free();
- if (s == NULL && s->size() <= 1) {
+ if (s == NULL || s->size() <= 1) {
debug(1, "empty animation");
return;
}
diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h
index dc195c0f4e..069e6e40d0 100644
--- a/engines/teenagent/teenagent.h
+++ b/engines/teenagent/teenagent.h
@@ -40,7 +40,7 @@ struct ADGameDescription;
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Teen Agent
*/
namespace TeenAgent {
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..4f871db8d2
--- /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..fd5088636f 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
@@ -520,7 +514,7 @@ void BMVPlayer::MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, CO
LoadSubString(stringId, 0, TextBufferAddr(), TBUFSZ);
texts[index].dieFrame = currentFrame + duration;
- texts[index].pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ texts[index].pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(),
0,
x, y,
diff --git a/engines/tinsel/coroutine.cpp b/engines/tinsel/coroutine.cpp
new file mode 100644
index 0000000000..a1871f90b0
--- /dev/null
+++ b/engines/tinsel/coroutine.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "tinsel/coroutine.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+namespace Tinsel {
+
+
+CoroContext nullContext = NULL;
+
+
+#if COROUTINE_DEBUG
+namespace {
+static int s_coroCount = 0;
+
+typedef Common::HashMap<Common::String, int> CoroHashMap;
+static CoroHashMap *s_coroFuncs = 0;
+
+static void changeCoroStats(const char *func, int change) {
+ if (!s_coroFuncs)
+ s_coroFuncs = new CoroHashMap();
+
+ (*s_coroFuncs)[func] += change;
+}
+
+static void displayCoroStats() {
+ printf("%d active coros\n", s_coroCount);
+
+ // Loop over s_coroFuncs and print info about active coros
+ if (!s_coroFuncs)
+ return;
+ for (CoroHashMap::const_iterator it = s_coroFuncs->begin();
+ it != s_coroFuncs->end(); ++it) {
+ if (it->_value != 0)
+ printf(" %3d x %s\n", it->_value, it->_key.c_str());
+ }
+}
+
+}
+#endif
+
+CoroBaseContext::CoroBaseContext(const char *func)
+ : _line(0), _sleep(0), _subctx(0) {
+#if COROUTINE_DEBUG
+ _funcName = func;
+ changeCoroStats(_funcName, +1);
+ s_coroCount++;
+#endif
+}
+
+CoroBaseContext::~CoroBaseContext() {
+#if COROUTINE_DEBUG
+ s_coroCount--;
+ changeCoroStats(_funcName, -1);
+ printf("Deleting coro in %s at %p (subctx %p)\n ",
+ _funcName, (void *)this, (void *)_subctx);
+ displayCoroStats();
+#endif
+ delete _subctx;
+}
+
+} // End of namespace Tinsel
+
diff --git a/engines/tinsel/coroutine.h b/engines/tinsel/coroutine.h
index d4cd54a8db..c8b220b212 100644
--- a/engines/tinsel/coroutine.h
+++ b/engines/tinsel/coroutine.h
@@ -27,6 +27,7 @@
#define TINSEL_COROUTINE_H
#include "common/scummsys.h"
+#include "common/util.h" // for SCUMMVM_CURRENT_FUNCTION
namespace Tinsel {
@@ -58,6 +59,10 @@ namespace Tinsel {
*/
//@{
+
+// Enable this macro to enable some debugging support in the coroutine code.
+//#define COROUTINE_DEBUG 1
+
/**
* The core of any coroutine context which captures the 'state' of a coroutine.
* Private use only.
@@ -66,8 +71,11 @@ struct CoroBaseContext {
int _line;
int _sleep;
CoroBaseContext *_subctx;
- CoroBaseContext() : _line(0), _sleep(0), _subctx(0) {}
- ~CoroBaseContext() { delete _subctx; }
+#if COROUTINE_DEBUG
+ const char *_funcName;
+#endif
+ CoroBaseContext(const char *func);
+ ~CoroBaseContext();
};
typedef CoroBaseContext *CoroContext;
@@ -101,9 +109,7 @@ public:
};
-#define CORO_PARAM CoroContext &coroParam
-
-#define CORO_SUBCTX coroParam->_subctx
+#define CORO_PARAM CoroContext &coroParam
/**
@@ -123,11 +129,10 @@ public:
* _ctx->var = 0;
*
* @see CORO_END_CONTEXT
- *
- * @note We always declare a variable 'DUMMY' to allow the user to specify
- * an 'empty' context.
*/
-#define CORO_BEGIN_CONTEXT struct CoroContextTag : CoroBaseContext { int DUMMY
+#define CORO_BEGIN_CONTEXT \
+ struct CoroContextTag : CoroBaseContext { \
+ CoroContextTag() : CoroBaseContext(SCUMMVM_CURRENT_FUNCTION) {} \
/**
* End the declaration of a coroutine context.
@@ -152,7 +157,10 @@ public:
* @see CORO_END_CODE
*/
#define CORO_END_CODE \
- if (&coroParam == &nullContext) nullContext = NULL; \
+ if (&coroParam == &nullContext) { \
+ delete nullContext; \
+ nullContext = NULL; \
+ } \
}
/**
@@ -174,11 +182,28 @@ public:
#define CORO_KILL_SELF() \
do { if (&coroParam != &nullContext) { coroParam->_sleep = -1; } return; } while (0)
+
+/**
+ * This macro is to be used in conjunction with CORO_INVOKE_ARGS and
+ * similar macros for calling coroutines-enabled subroutines.
+ */
+#define CORO_SUBCTX coroParam->_subctx
+
/**
* Invoke another coroutine.
*
* What makes this tricky is that the coroutine we called my yield/sleep,
* and we need to deal with this adequately.
+ *
+ * @param subCoro name of the coroutine-enabled function to invoke
+ * @param ARGS list of arguments to pass to subCoro
+ *
+ * @note ARGS must be surrounded by parentheses, and the first argument
+ * in this list must always be CORO_SUBCTX. For example, the
+ * regular function call
+ * myFunc(a, b);
+ * becomes the following:
+ * CORO_INVOKE_ARGS(myFunc, (CORO_SUBCTX, a, b));
*/
#define CORO_INVOKE_ARGS(subCoro, ARGS) \
do {\
diff --git a/engines/tinsel/debugger.cpp b/engines/tinsel/debugger.cpp
index f422c88e94..5d8b22116d 100644
--- a/engines/tinsel/debugger.cpp
+++ b/engines/tinsel/debugger.cpp
@@ -57,7 +57,8 @@ int strToInt(const char *s) {
// Hexadecimal string
uint tmp;
- sscanf(s, "%xh", &tmp);
+ if (!sscanf(s, "%xh", &tmp))
+ tmp = 0;
return (int)tmp;
}
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
index a2a32d2e13..239e06c8dd 100644
--- a/engines/tinsel/detection_tables.h
+++ b/engines/tinsel/detection_tables.h
@@ -102,6 +102,75 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
{ // Floppy V1 version, with *.gra files
{
"dw",
@@ -419,6 +488,26 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
+ { // English DW2 demo
+ {
+ "dw2",
+ "Demo",
+ {
+ {"dw2.scn", 0, "853ab998f5136b69bc586991175d6eeb", 4231121},
+ {"english.smp", 0, "b5660a0e031cb4710bcb0ef5629ea61d", 28562357},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES | GF_DEMO,
+ TINSEL_V2,
+ },
+
{ // European/Australian Discworld 2 release
{
"dw2",
diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp
index ed59b5669b..135db71575 100644
--- a/engines/tinsel/dialogs.cpp
+++ b/engines/tinsel/dialogs.cpp
@@ -1587,7 +1587,7 @@ static bool InvKeyIn(const Common::KeyState &kbd) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
iconArray[HL3] = NULL;
}
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
@@ -1595,7 +1595,7 @@ static bool InvKeyIn(const Common::KeyState &kbd) {
if (MultiRightmost(iconArray[HL3]) > MAX_NAME_RIGHT) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), iconArray[HL3]);
UpdateString(Common::KeyState(Common::KEYCODE_BACKSPACE));
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[cd.selBox].xpos + 2,
InvD[ino].inventoryY + cd.box[cd.selBox].ypos + TYOFF,
@@ -1669,7 +1669,7 @@ static void Select(int i, bool force) {
}
#endif
- iconArray[HL3] = ObjectTextOut(nullContext,
+ iconArray[HL3] = ObjectTextOut(
GetPlayfieldList(FIELD_STATUS), sedit, 0,
InvD[ino].inventoryX + cd.box[i].xpos + 2,
#ifdef JAPAN
@@ -2634,17 +2634,16 @@ static void AddBackground(OBJECT **rect, OBJECT **title, int extraH, int extraV,
return;
// Create text object using title string
- CoroContext dummyCoro;
if (textFrom == FROM_HANDLE) {
LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
MultiSetZPosition(*title, Z_INV_HTEXT);
} else if (textFrom == FROM_STRING && cd.ixHeading != NO_HEADING) {
LoadStringRes(configStrings[cd.ixHeading], TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(dummyCoro, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + width/2, InvD[ino].inventoryY + M_TOFF,
GetTagFontHandle(), TXT_CENTRE);
assert(*title); // Inventory title string produced NULL text
@@ -2668,7 +2667,7 @@ static void AddTitle(POBJECT *title, int extraH) {
// Create text object using title string
if (InvD[ino].hInvTitle != (SCNHANDLE)NO_HEADING) {
LoadStringRes(InvD[ino].hInvTitle, TextBufferAddr(), TBUFSZ);
- *title = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ *title = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
InvD[ino].inventoryX + (width/2)+NM_BG_POS_X, InvD[ino].inventoryY + NM_TOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
assert(*title);
@@ -2749,14 +2748,14 @@ static void AddBox(int *pi, const int i) {
(!TinselV2 && (cd.box[i].ixText == USE_POINTER))) {
if (cd.box[i].boxText != NULL) {
if (cd.box[i].boxType == RGROUP) {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
x + 2, y+2, GetTagFontHandle(), 0);
#else
x + 2, y + TYOFF, GetTagFontHandle(), 0);
#endif
} else {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), cd.box[i].boxText, 0,
#ifdef JAPAN
// Note: it never seems to go here!
x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
@@ -2782,10 +2781,10 @@ static void AddBox(int *pi, const int i) {
}
if (TinselV2 && (cd.box[i].boxType == RGROUP))
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, x + 2, y + TYOFF, GetTagFontHandle(), 0, 0);
else
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0,
#ifdef JAPAN
x + cd.box[i].w/2, y+2, GetTagFontHandle(), TXT_CENTRE);
@@ -2842,7 +2841,7 @@ static void AddBox(int *pi, const int i) {
assert(cd.box[i].ixText != USE_POINTER);
LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
}
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -2869,11 +2868,11 @@ static void AddBox(int *pi, const int i) {
}
if (cd.box[i].boxType == TOGGLE2) {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
} else {
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + MDTEXT_XOFF, y + MDTEXT_YOFF,
GetTagFontHandle(), TXT_RIGHT, 0);
}
@@ -2908,7 +2907,7 @@ static void AddBox(int *pi, const int i) {
assert(cd.box[i].ixText != USE_POINTER);
LoadStringRes(configStrings[cd.box[i].ixText], TextBufferAddr(), TBUFSZ);
}
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x+MDTEXT_XOFF, y+MDTEXT_YOFF, GetTagFontHandle(), TXT_RIGHT);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
@@ -2933,7 +2932,7 @@ static void AddBox(int *pi, const int i) {
// Stick in the text
assert(cd.box[i].textMethod == TM_INDEX);
LoadStringRes(SysString(cd.box[i].ixText), TextBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x + cd.box[i].w / 2, y + TOG2_YOFF,
GetTagFontHandle(), TXT_CENTRE, 0);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
@@ -2945,7 +2944,7 @@ static void AddBox(int *pi, const int i) {
break;
LoadStringRes(LanguageDesc(displayedLanguage), TextBufferAddr(), TBUFSZ);
- iconArray[*pi] = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
+ iconArray[*pi] = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), 0,
x + cd.box[i].w / 2, y + ROT_YOFF, GetTagFontHandle(), TXT_CENTRE, 0);
MultiSetZPosition(iconArray[*pi], Z_INV_ITEXT);
*pi += 1;
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index 48270d94e3..aefc6e6144 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -66,8 +66,8 @@ uint8* psxPJCRLEUnwinder(uint16 imageWidth, uint16 imageHeight, uint8 *srcIdx) {
return NULL;
// Calculate needed index numbers, align width and height not next multiple of four
- imageWidth = imageWidth % 4 ? ((imageWidth / 4) + 1) * 4 : imageWidth;
- imageHeight = imageHeight % 4 ? ((imageHeight / 4) + 1) * 4 : imageHeight;
+ imageWidth = (imageWidth % 4) ? ((imageWidth / 4) + 1) * 4 : imageWidth;
+ imageHeight = (imageHeight % 4) ? ((imageHeight / 4) + 1) * 4 : imageHeight;
destinationBuffer = (uint8*)malloc((imageWidth * imageHeight) / 8);
dstIdx = destinationBuffer;
remainingBlocks = (imageWidth * imageHeight) / 16;
@@ -297,7 +297,7 @@ static void PsxDrawTiles(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply
} else {
for (int xp = boxBounds.left; xp <= boxBounds.right; ++xp) {
// Extract pixel value from byte
- byte pixValue = (*(p + (xp / 2)) & (xp % 2 ? 0xf0 : 0x0f)) >> (xp % 2 ? 4 : 0);
+ byte pixValue = (*(p + (xp / 2)) & ((xp % 2) ? 0xf0 : 0x0f)) >> ((xp % 2) ? 4 : 0);
if (pixValue || !transparency)
*(tempDest + SCREEN_WIDTH * (yp - boxBounds.top) + (xp - boxBounds.left)) = psxMapperTable[pixValue];
}
diff --git a/engines/tinsel/module.mk b/engines/tinsel/module.mk
index 6c818bcb0f..2778cec3df 100644
--- a/engines/tinsel/module.mk
+++ b/engines/tinsel/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS := \
bmv.o \
cliprect.o \
config.o \
+ coroutine.o \
cursor.o \
debugger.o \
detection.o \
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index ea34fa963a..9560624e05 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -158,7 +158,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
@@ -967,8 +971,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);
}
}
diff --git a/engines/tinsel/pdisplay.cpp b/engines/tinsel/pdisplay.cpp
index e05a6f6a9a..d55f8be260 100644
--- a/engines/tinsel/pdisplay.cpp
+++ b/engines/tinsel/pdisplay.cpp
@@ -155,7 +155,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
- _ctx->cpText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
if (DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
@@ -167,7 +167,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
PolyCornerX(hp, 1), PolyCornerY(hp, 1),
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
- _ctx->cpathText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, GetTagFontHandle(), 0);
}
@@ -213,7 +213,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// create new text object list
sprintf(PositionString, "%d %d", aniX, aniY);
- _ctx->rpText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous position
@@ -232,7 +232,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
}
sprintf(PositionString, "String: %d", newestString);
- _ctx->spText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), PositionString,
+ _ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
// update previous value
@@ -407,7 +407,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
// May have buggered cursor
EndCursorFollowed();
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), tagBuffer,
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tagBuffer,
0, tagX, tagY, GetTagFontHandle(), TXT_CENTRE, 0);
assert(*ppText);
MultiSetZPosition(*ppText, Z_TAG_TEXT);
@@ -452,7 +452,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
@@ -555,7 +555,7 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
// May have buggered cursor
EndCursorFollowed();
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE, 0);
} else if (TinselV2) {
@@ -565,11 +565,11 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
StartCursorFollowed();
GetCursorXYNoWait(&curX, &curY, false);
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, curX, curY, GetTagFontHandle(), TXT_CENTRE, 0);
} else {
// Handle displaying the tag text on-screen
- *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ *ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
diff --git a/engines/tinsel/rince.cpp b/engines/tinsel/rince.cpp
index cfffe88587..17b2bf4ddc 100644
--- a/engines/tinsel/rince.cpp
+++ b/engines/tinsel/rince.cpp
@@ -361,8 +361,8 @@ static void InitMover(PMOVER pMover) {
pMover->Tline = 0;
- if (pMover->direction != FORWARD || pMover->direction != AWAY
- || pMover->direction != LEFTREEL || pMover->direction != RIGHTREEL)
+ if (pMover->direction != FORWARD && pMover->direction != AWAY
+ && pMover->direction != LEFTREEL && pMover->direction != RIGHTREEL)
pMover->direction = FORWARD;
if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
diff --git a/engines/tinsel/scene.h b/engines/tinsel/scene.h
index e17a6ab7a0..2ef7da1289 100644
--- a/engines/tinsel/scene.h
+++ b/engines/tinsel/scene.h
@@ -78,9 +78,9 @@ enum REEL {
typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
// amount to shift scene handles by
-#define SCNHANDLE_SHIFT (TinselV2 ? 25 : 23)
-#define OFFSETMASK (TinselV2 ? 0x01ffffffL : 0x007fffffL)
-#define HANDLEMASK (TinselV2 ? 0xFE000000L : 0xFF800000L)
+#define SCNHANDLE_SHIFT ((TinselV2 && !IsDemo) ? 25 : 23)
+#define OFFSETMASK ((TinselV2 && !IsDemo) ? 0x01ffffffL : 0x007fffffL)
+#define HANDLEMASK ((TinselV2 && !IsDemo) ? 0xFE000000L : 0xFF800000L)
void DoHailScene(SCNHANDLE scene);
diff --git a/engines/tinsel/sched.cpp b/engines/tinsel/sched.cpp
index d8a44944fc..97e41e2a7d 100644
--- a/engines/tinsel/sched.cpp
+++ b/engines/tinsel/sched.cpp
@@ -45,8 +45,6 @@ struct PROCESS_STRUC {
#include "common/pack-end.h" // END STRUCT PACKING
-CoroContext nullContext = NULL;
-
//----------------- LOCAL GLOBAL DATA --------------------
static uint32 numSceneProcess;
@@ -77,6 +75,14 @@ Scheduler::Scheduler() {
}
Scheduler::~Scheduler() {
+ // Kill all running processes (i.e. free memory allocated for their state).
+ PROCESS *pProc = active->pNext;
+ while (pProc != NULL) {
+ delete pProc->state;
+ pProc->state = 0;
+ pProc = pProc->pNext;
+ }
+
free(processList);
processList = NULL;
@@ -392,6 +398,7 @@ void Scheduler::killProcess(PROCESS *pKillProc) {
(pRCfunction)(pKillProc);
delete pKillProc->state;
+ pKillProc->state = 0;
// Take the process out of the active chain list
pKillProc->pPrevious->pNext = pKillProc->pNext;
@@ -458,6 +465,7 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
(pRCfunction)(pProc);
delete pProc->state;
+ pProc->state = 0;
// make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext;
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index ce2ed51d09..0a32ab143f 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;
@@ -471,8 +480,8 @@ void SoundManager::setSFXVolumes(uint8 volume) {
* Opens and inits all sound sample files.
*/
void SoundManager::openSampleFiles() {
- // Floppy and demo versions have no sample files
- if (_vm->getFeatures() & GF_FLOPPY || _vm->getFeatures() & GF_DEMO)
+ // Floppy and demo versions have no sample files, except for the Discworld 2 demo
+ if (_vm->getFeatures() & GF_FLOPPY || (IsDemo && !TinselV2))
return;
TinselFile f;
diff --git a/engines/tinsel/text.cpp b/engines/tinsel/text.cpp
index c7c921b935..d2939281eb 100644
--- a/engines/tinsel/text.cpp
+++ b/engines/tinsel/text.cpp
@@ -107,7 +107,7 @@ int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
* @param mode Mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
-OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification
int yOffset; // offset to next line of text
diff --git a/engines/tinsel/text.h b/engines/tinsel/text.h
index 664f0d207c..a849e286ec 100644
--- a/engines/tinsel/text.h
+++ b/engines/tinsel/text.h
@@ -98,7 +98,7 @@ struct TEXTOUT {
* @param mode mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
-OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
+OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime = 0);
OBJECT *ObjectTextOutIndirect( // output a string of text
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 766d4ed54a..dea60802d1 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -213,6 +213,43 @@ const MASTER_LIB_CODES DW1_CODES[] = {
HIGHEST_LIBCODE
};
+const MASTER_LIB_CODES DW2DEMO_CODES[] = {
+ ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
+ ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
+ ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
+ BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
+ CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
+ CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
+ CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
+ DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
+ DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
+ DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
+ ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
+ FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, HASRESTARTED,
+ HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
+ HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, INSTANTSCROLL,
+ INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLGLOBALPROCESS,
+ KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
+ NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET, OTHEROBJECT, PAUSE, PLAY,
+ PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR,
+ POSTGLOBALPROCESS, POSTOBJECT, POSTPROCESS, POSTTAG, PRINT,
+ PRINTCURSOR, PRINTOBJ, PRINTTAG, QUITGAME, RANDOM, RESETIDLETIME,
+ RESTARTGAME, RESTORESCENE, RUNMODE, SAVESCENE, SAY, SAYAT,
+ SCALINGREELS, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS,
+ SENDACTOR, SENDGLOBALPROCESS, SENDOBJECT, SENDPROCESS, SENDTAG,
+ SETBRIGHTNESS, SETINVLIMIT, SETINVSIZE, SETLANGUAGE, SETPALETTE,
+ SETSYSTEMSTRING, SETSYSTEMVAR, SHELL, SHOWACTOR, SHOWBLOCK,
+ SHOWEFFECT, SHOWPATH, SHOWREFER, SHOWTAG, STAND, STANDTAG,
+ STARTGLOBALPROCESS, STARTPROCESS, STARTTIMER, STOPWALK, SUBTITLES,
+ SWALK, SYSTEMVAR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS,
+ TALK, TALKAT, TALKPALETTEINDEX, TALKRGB, TALKVIA, THISOBJECT,
+ THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
+ UNDIMMUSIC, UNHOOKSCENE, WAITFRAME, WAITKEY, WAITSCROLL, WAITTIME,
+ WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
+ WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY,
+ HIGHEST_LIBCODE
+};
+
const MASTER_LIB_CODES DW2_CODES[] = {
ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
@@ -1912,7 +1949,7 @@ static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSust
if (TinselV2) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(),
TXT_CENTRE, 0);
assert(_ctx->pText);
@@ -1925,7 +1962,7 @@ static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSust
} else if (bJapDoPrintText || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
int Loffset, Toffset; // Screen position
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, x - Loffset, y - Toffset,
TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // string produced NULL text
@@ -2089,7 +2126,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
else
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // PrintObj() string produced NULL text
@@ -2141,7 +2178,7 @@ static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo,
// Re-display in the same place
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
- _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, _ctx->textx, _ctx->texty, GetTagFontHandle(),
TXT_CENTRE, 0);
assert(_ctx->pText);
@@ -2258,7 +2295,7 @@ static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *
// Re-display in the same place
LoadStringRes(text, TextBufferAddr(), TBUFSZ);
- pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
+ pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, textx, texty, GetTagFontHandle(), TXT_CENTRE);
assert(pText); // PrintObj() string produced NULL text
MultiSetZPosition(pText, Z_INV_ITEXT);
@@ -3327,7 +3364,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x
_ctx->y -= _ctx->Toffset;
}
- _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+ _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
GetTalkFontHandle(), TXT_CENTRE);
assert(_ctx->pText); // talk() string produced NULL text;
@@ -3376,7 +3413,7 @@ static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x
// Kick off the sample now (perhaps with a delay)
if (bNoPause)
bNoPause = false;
- else
+ else if (!IsDemo)
CORO_SLEEP(SysVar(SV_SPEECHDELAY));
//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
@@ -4208,6 +4245,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
int libCode;
if (TinselV0) libCode = DW1DEMO_CODES[operand];
else if (!TinselV2) libCode = DW1_CODES[operand];
+ else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand];
else libCode = DW2_CODES[operand];
debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
@@ -4907,9 +4945,6 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
else {
Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false,
pic->event, pic->hPoly, pic->idActor);
-
- if (coroParam)
- return 0;
}
return -4;
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 248c97e209..bb6ed99138 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -146,7 +146,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) {
@@ -868,6 +867,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);
@@ -1169,7 +1173,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 ed70979349..c4f4a3bc13 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -48,7 +48,7 @@
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Discworld
* - Discworld 2: Missing Presumed ...!?
*/
@@ -139,6 +139,7 @@ typedef bool (*KEYFPTR)(const Common::KeyState &);
#define TinselV1 (TinselVersion == TINSEL_V1)
#define TinselV2 (TinselVersion == TINSEL_V2)
#define TinselV1PSX (TinselVersion == TINSEL_V1 && _vm->getPlatform() == Common::kPlatformPSX)
+#define IsDemo (_vm->getFeatures() & GF_DEMO)
// Global reference to the TinselEngine object
extern TinselEngine *_vm;
@@ -183,6 +184,7 @@ public:
uint32 getFeatures() const;
Common::Language getLanguage() const;
uint16 getVersion() const;
+ uint32 getFlags() const;
Common::Platform getPlatform() const;
const char *getSampleIndex(LANGUAGE lang);
diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp
new file mode 100644
index 0000000000..53980e3dc1
--- /dev/null
+++ b/engines/toon/anim.cpp
@@ -0,0 +1,738 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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];
+ if (compressedSize < decompressedSize) {
+ decompressLZSS(imageData, _frames[e]._data, decompressedSize);
+ } else {
+ memcpy(_frames[e]._data, imageData, compressedSize);
+ }
+ }
+
+ 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;
+ int32 offsX = 0;
+ int32 offsY = 0;
+
+ if (xx + _x1 + _frames[frame]._x1 < 0) {
+ offsX = -(xx + _x1 + _frames[frame]._x1);
+ }
+
+ if (offsX >= rectX)
+ return;
+ else
+ rectX -= offsX;
+
+ if (yy + _y1 + _frames[frame]._y1 < 0) {
+ offsY = -(yy + _y1 + _frames[frame]._y1);
+ }
+
+ if (offsY >= rectY)
+ return;
+ else
+ rectY -= offsY;
+
+ 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 + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY;
+ uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1 + offsY) * destPitch + (xx + _x1 + _frames[frame]._x1 + offsX);
+ 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;
+ _alignBottom = 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;
+
+ int32 x = _x;
+ int32 y = _y;
+
+ if (_alignBottom) {
+ int32 offsetX = (_animation->_x2 - _animation->_x1) / 2 * (_scale - 1024);
+ int32 offsetY = (_animation->_y2 - _animation->_y1) * (_scale - 1024);
+
+ x -= offsetX >> 10;
+ y -= offsetY >> 10;
+ }
+
+ 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, bool align) {
+ debugC(4, kDebugAnim, "setScale(%d)", scale);
+ _scale = scale;
+ _alignBottom = align;
+}
+
+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..01475276c5
--- /dev/null
+++ b/engines/toon/anim.h
@@ -0,0 +1,196 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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, bool align = false);
+ 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;
+ bool _alignBottom;
+
+ 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 _frozenForConversation;
+ 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..4381dad6f7
--- /dev/null
+++ b/engines/toon/audio.cpp
@@ -0,0 +1,629 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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;
+
+ for (int32 i = 0; i < 4; i++)
+ _audioPacks[i] = 0;
+
+ for (int32 i = 0; i < 4; i++) {
+ _ambientSFXs[i]._delay = 0;
+ _ambientSFXs[i]._enabled = false;
+ _ambientSFXs[i]._id = -1;
+ _ambientSFXs[i]._channel = -1;
+ _ambientSFXs[i]._lastTimer = 0;
+ _ambientSFXs[i]._volume = 255;
+ }
+
+ _voiceMuted = false;
+ _musicMuted = false;
+ _sfxMuted = false;
+}
+
+AudioManager::~AudioManager(void) {
+ for (int32 i = 0; i < 4; i++) {
+ closeAudioPack(i);
+ }
+}
+
+void AudioManager::muteMusic(bool muted) {
+ setMusicVolume(muted ? 0 : 255);
+ _musicMuted = muted;
+}
+
+void AudioManager::muteVoice(bool muted) {
+ if(voiceStillPlaying() && _channels[2]) {
+ _channels[2]->setVolume(muted ? 0 : 255);
+ }
+ _voiceMuted = muted;
+}
+
+void AudioManager::muteSfx(bool muted) {
+ _sfxMuted = muted;
+}
+
+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]->setVolume(_musicMuted ? 0 : 255);
+ _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);
+ _channels[2]->setVolume(_voiceMuted ? 0 : 255);
+
+}
+
+int32 AudioManager::playSFX(int32 id, int 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 -1;
+
+ 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(_sfxMuted ? 0 : volume);
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void AudioManager::stopAllSfxs() {
+ for (int32 i = 3; i < 16; i++) {
+ if (_channels[i] && _channels[i]->isPlaying()) {
+ _channels[i]->stop(false);
+ }
+ }
+}
+
+void AudioManager::stopCurrentVoice() {
+ debugC(1, kDebugAudio, "stopCurrentVoice()");
+
+ if (_channels[2] && _channels[2]->isPlaying())
+ _channels[2]->stop(false);
+}
+
+
+void AudioManager::closeAudioPack(int32 id) {
+ if(_audioPacks[id]) {
+ delete _audioPacks[id];
+ _audioPacks[id] = 0;
+ }
+}
+
+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());
+
+ closeAudioPack(id);
+ _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();
+ }
+}
+
+int 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, -1);
+ handleFade(0);
+}
+
+void AudioStreamInstance::handleFade(int32 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;
+ if (_file) {
+ delete _file;
+ _file = 0;
+ }
+}
+
+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);
+ }
+}
+
+void AudioManager::startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume)
+{
+ int32 found = -1;
+ for (int32 i = 0; i < 4; i++) {
+ if (!_ambientSFXs[i]._enabled) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found < 0)
+ return;
+
+ _ambientSFXs[found]._lastTimer = _vm->getOldMilli() - 1;
+ _ambientSFXs[found]._delay = delay;
+ _ambientSFXs[found]._enabled = true;
+ _ambientSFXs[found]._mode = mode;
+ _ambientSFXs[found]._volume = volume;
+ _ambientSFXs[found]._id = id;
+ updateAmbientSFX();
+
+}
+
+void AudioManager::setAmbientSFXVolume(int32 id, int volume) {
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_id == id && ambient->_enabled) {
+ ambient->_volume = volume;
+ if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
+ _channels[ambient->_channel]->setVolume(volume);
+ }
+ break;
+ }
+ }
+}
+
+void AudioManager::killAmbientSFX(int32 id)
+{
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_id == id && ambient->_enabled) {
+ ambient->_enabled = false;
+ ambient->_id = -1;
+
+ if (_channels[ambient->_channel]) {
+ _channels[ambient->_channel]->stop(false);
+ }
+ }
+
+ }
+}
+
+void AudioManager::killAllAmbientSFX()
+{
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_enabled) {
+ ambient->_enabled = false;
+ ambient->_id = -1;
+
+ if (_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
+ _channels[ambient->_channel]->stop(false);
+ }
+ }
+ }
+}
+
+void AudioManager::updateAmbientSFX()
+{
+ if (_vm->getMoviePlayer()->isPlaying()) return;
+
+ for (int32 i = 0; i < 4; i++) {
+ AudioAmbientSFX* ambient = &_ambientSFXs[i];
+ if (ambient->_enabled && (ambient->_channel < 0 || !(_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()))) {
+ if(ambient->_mode == 1) {
+ if (_vm->randRange(0, 32767) < ambient->_delay) {
+ ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
+ }
+ } else {
+ if (_vm->getOldMilli() > ambient->_lastTimer) {
+ ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
+ ambient->_lastTimer = _vm->getOldMilli(); // + 60 * _vm->getTickLength() * ambient->_delay;
+ }
+ }
+ }
+ }
+}
+
+
+} // End of namespace Toon
+
diff --git a/engines/toon/audio.h b/engines/toon/audio.h
new file mode 100644
index 0000000000..e0676c2992
--- /dev/null
+++ b/engines/toon/audio.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$
+ *
+ */
+
+#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:
+ int 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;
+};
+
+struct AudioAmbientSFX {
+ int32 _id;
+ int32 _volume;
+ int32 _lastTimer;
+ int32 _delay;
+ int32 _mode;
+ int32 _channel;
+ bool _enabled;
+};
+
+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);
+ int32 playSFX(int32 id, int volume, bool genericSFX);
+ void stopCurrentVoice();
+ void stopAllSfxs();
+ void setMusicVolume(int32 volume);
+ void stopMusic();
+ void muteVoice(bool mute);
+ void muteMusic(bool mute);
+ void muteSfx(bool mute);
+ bool isVoiceMuted() { return _voiceMuted; }
+ bool isMusicMuted() { return _musicMuted; }
+ bool isSfxMuted() { return _sfxMuted; }
+
+ void startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume);
+ void killAmbientSFX(int32 id);
+ void killAllAmbientSFX();
+ void updateAmbientSFX();
+ void setAmbientSFXVolume(int32 id, int volume);
+
+ void closeAudioPack(int32 id);
+ 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;
+
+protected:
+ bool _voiceMuted;
+ bool _musicMuted;
+ bool _sfxMuted;
+
+ AudioAmbientSFX _ambientSFXs[4];
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp
new file mode 100644
index 0000000000..f6e244a064
--- /dev/null
+++ b/engines/toon/character.cpp
@@ -0,0 +1,1043 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with 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;
+ _isTalking = false;
+ _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<int32>(5, 40 * _vm->getDrew()->getScale() / 1024);
+ int32 sizeY = MAX<int32>(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<int32>(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::updateTimers(int32 relativeAdd) {
+ _nextIdleTime += relativeAdd;
+ _lastWalkTime += relativeAdd;
+}
+
+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)->_frozenForConversation = 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<int32>(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)->_frozenForConversation = true;
+
+
+ // TODO setup backup //
+
+ _animFlags |= 0x10;
+ _animationInstance->setAnimation(_specialAnim);
+ _animationInstance->setFrame(0);
+ _time = _vm->getOldMilli() + 8 * _vm->getTickLength();
+ }
+
+ }
+
+ if ((_animFlags & 3) == 2) {
+ if ((((_animFlags & 8) == 8) && _vm->getCurrentLineToSay() != _lineToSayId) || !_vm->getAudioManager()->voiceStillPlaying()) // || (_flags & 8)) && _vm->getAudioManager()->voiceStillPlaying())
+ _animFlags |= 1;
+
+ }
+
+ 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)
+ printf(" drew animation name %s / flag %d / frame %d \n", _specialAnim->_name, _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 && (flags & 8) == 0)
+ _vm->getSceneAnimationScript(_animScriptId)->_frozenForConversation = true;
+
+ stopSpecialAnim();
+
+ if (flags & 8) {
+ // talker
+ _lineToSayId = _vm->getCurrentLineToSay();
+
+ // make the talker busy
+ _flags |= 1;
+
+ // wait for the character to be ready
+ while (_animScriptId != -1 && _animationInstance->getFrame() > 0 && (_specialAnim && _animationInstance->getAnimation() != _specialAnim)) {
+ _vm->simpleUpdate(false);
+ }
+ }
+
+
+ if (_sceneAnimationId > -1)
+ setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance);
+
+
+ _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..43636b8eb5
--- /dev/null
+++ b/engines/toon/character.h
@@ -0,0 +1,148 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along 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; }
+ virtual void updateTimers(int32 relativeAdd);
+ virtual void setTalking(bool talking) { _isTalking = talking; }
+ virtual bool isTalking() { return _isTalking; }
+
+ 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;
+ bool _isTalking;
+
+ 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..e9649b1560
--- /dev/null
+++ b/engines/toon/detection.cpp
@@ -0,0 +1,273 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with 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);
+}
+
+void ToonMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::printf("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+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..8b042f499d
--- /dev/null
+++ b/engines/toon/font.cpp
@@ -0,0 +1,285 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with 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] = {
+ '?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
+ '?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx
+ '?', '?', '?', '?', 0x1d, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 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
+};
+
+byte FontRenderer::textToFont(byte c) {
+ // No need to remap simple characters.
+ if (c < 0x80)
+ return c;
+
+ // The Spanish version shows grave accent over the 'e' when it should
+ // be acute. This happens both in the original interpreter and when
+ // using the common map which works for other languages, so we add a
+ // special case for it.
+ if (_vm->_language == Common::ES_ESP && c == 0xe9)
+ return 0x10;
+
+ // Use the common map to convert the extended characters.
+ return map_textToFont[c - 0x80];
+}
+
+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 {
+ curChar = textToFont(curChar);
+ _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 {
+ curChar = textToFont(curChar);
+ 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
+ curChar = textToFont(curChar);
+
+ 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 = textToFont(*line);
+ 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..e1b00fbf54
--- /dev/null
+++ b/engines/toon/font.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$
+*
+*/
+
+#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];
+ byte textToFont(byte c);
+};
+
+} // 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..91ea98a91f
--- /dev/null
+++ b/engines/toon/movie.cpp
@@ -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$
+*
+*/
+
+#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;
+ _playing = false;
+ _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);
+
+ _playing = true;
+ if (flags & 1)
+ _vm->getAudioManager()->setMusicVolume(0);
+ _decoder->loadFile(video.c_str(), flags);
+ playVideo();
+ _vm->flushPalette();
+ if (flags & 1)
+ _vm->getAudioManager()->setMusicVolume(_vm->getAudioManager()->isMusicMuted() ? 0 : 255);
+ _decoder->close();
+ _playing = false;
+}
+
+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..4d5efb3343
--- /dev/null
+++ b/engines/toon/movie.h
@@ -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$
+*
+*/
+
+#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);
+ bool isPlaying() { return _playing; }
+
+protected:
+ bool playVideo();
+ ToonEngine *_vm;
+ Audio::Mixer *_mixer;
+ ToonstruckSmackerDecoder *_decoder;
+ bool _playing;
+
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/path.cpp b/engines/toon/path.cpp
new file mode 100644
index 0000000000..cec9c7dbf0
--- /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;
+}
+
+int32 PathFindingHeap::unload() {
+ if (_data)
+ delete[] _data;
+ return 0;
+}
+
+int32 PathFindingHeap::clear() {
+ //debugC(1, kDebugPath, "clear()");
+
+ _count = 0;
+ memset(_data, 0, sizeof(HeapDataGrid) * _alloc * 2);
+ return 1;
+}
+
+int32 PathFindingHeap::push(int32 x, int32 y, int32 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;
+ }
+}
+
+int32 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<int32>(curX + 1, _width - 1);
+ int32 endY = MIN<int32>(curY + 1, _height - 1);
+ int32 startX = MAX<int32>(curX - 1, 0);
+ int32 startY = MAX<int32>(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<int32>(curX + 1, _width - 1);
+ int32 endY = MIN<int32>(curY + 1, _height - 1);
+ int32 startX = MAX<int32>(curX - 1, 0);
+ int32 startY = MAX<int32>(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..31d9f94f36
--- /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 OpcodeV2 *> *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..d7f45096c2
--- /dev/null
+++ b/engines/toon/script.h
@@ -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$
+*
+*/
+
+#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;
+class ScriptFunc;
+typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
+
+struct EMCData {
+ char filename[13];
+
+ byte *text;
+ uint16 *data;
+ uint16 *ordr;
+ uint16 dataSize;
+
+ const Common::Array<const OpcodeV2 *> *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 OpcodeV2 *> *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..f571f324ef
--- /dev/null
+++ b/engines/toon/script_func.cpp
@@ -0,0 +1,1186 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with 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 {
+
+#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 OpcodeV2 *> *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) {
+ int32 animID = stackPos(0);
+ int32 scale = stackPos(1);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animID);
+ if (sceneAnim) {
+ sceneAnim->_animInstance->setUseMask(true);
+ sceneAnim->_animInstance->setScale(scale,true);
+ }
+ 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) {
+ if (_vm->state()->_inCloseUp) {
+ _vm->getSystem()->warpMouse(stackPos(0), stackPos(1));
+ } else {
+ _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->isTalking();
+ 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, sceneAnim->_animInstance->getZ(), 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);
+ }
+
+ // WORKAROUND : Slow down just a little the guards dance animation so that the voices don't cut
+ if (_vm->state()->_currentScene == 2 && (sceneId == 2 || sceneId == 3)) {
+ waitTicks = 7;
+ _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 x = stackPos(2);
+ int32 layerZ = stackPos(3);
+
+ if (dx == -2)
+ sceneAnim->_animInstance->moveRelative(640, 0, 0);
+ else if (dx < 0) {
+ dx = sceneAnim->_animation->_x1;
+ }
+ 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(x, layerZ));
+ sceneAnim->_animInstance->setUseMask(true);
+ }
+
+ 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->setAnimation(sceneAnim->_animation);
+ 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) {
+ //printf("Ambient Sfx : %d %d %d %d\n", stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Kill_Ambient_Sfx(EMCState *state) {
+ //printf("Kill Sfx : %d \n", stackPos(0));
+ _vm->getAudioManager()->killAmbientSFX(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx_Plus(EMCState *state) {
+ //printf("Ambient Sfx Plus: %d %d %d %d %d %d %d %d\n", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7));
+ _vm->getAudioManager()->startAmbientSFX(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Ambient_Volume(EMCState *state) {
+ //printf("Ambient Volume : %d %d \n", stackPos(0), stackPos(1));
+ _vm->getAudioManager()->setAmbientSFXVolume(stackPos(0), stackPos(1));
+ 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..76b7b0ada1
--- /dev/null
+++ b/engines/toon/script_func.h
@@ -0,0 +1,174 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along 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;
+
+typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
+
+class ScriptFunc {
+public:
+ ScriptFunc(ToonEngine *vm);
+ ~ScriptFunc(void);
+ Common::Array<const OpcodeV2 *> _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..7a6a2fc87a
--- /dev/null
+++ b/engines/toon/toon.cpp
@@ -0,0 +1,4548 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with 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(1);
+ _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) {
+ if(canSaveGameStateCurrently())
+ saveGame(-1, Common::String());
+ }
+ if (event.kbd.keycode == Common::KEYCODE_F6) {
+ if(canLoadGameStateCurrently())
+ loadGame(-1);
+ }
+ if (event.kbd.ascii == 't') {
+ _showConversationText = !_showConversationText;
+ }
+ if (event.kbd.ascii == 'm') {
+ _audioManager->muteMusic(!_audioManager->isMusicMuted());
+ }
+ if (event.kbd.ascii == 'd') {
+ _audioManager->muteVoice(!_audioManager->isVoiceMuted());
+ }
+ if (event.kbd.ascii == 's') {
+ _audioManager->muteSfx(!_audioManager->isSfxMuted());
+ }
+
+ if (event.kbd.flags & Common::KBD_ALT) {
+ int32 slotNum = event.kbd.ascii - '0';
+ if (slotNum >= 0 && slotNum <= 9 && canSaveGameStateCurrently()) {
+ if (saveGame(slotNum, Common::String())) {
+ // 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 && canLoadGameStateCurrently()) {
+ 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);
+ _audioManager->updateAmbientSFX();
+ _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::doMagnifierEffect() {
+ int32 posX = _mouseX + state()->_currentScrollValue - _cursorOffsetX;
+ int32 posY = _mouseY - _cursorOffsetY - 2;
+
+ Graphics::Surface &surface = *_mainSurface;
+
+ // fast sqrt table lookup (values up to 144 only)
+ static const byte intSqrt[] = {
+ 0, 1, 1, 1, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 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, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 12
+ };
+
+ byte tempBuffer[25*25];
+ for (int32 y = -12; y <= 12; y++) {
+ for (int32 x = -12; x <= 12; x++) {
+ int32 destPitch = surface.pitch;
+ uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x);
+ tempBuffer[(y+12) * 25 + x + 12] = *curRow;
+ }
+ }
+
+ for (int32 y = -12; y <= 12; y++) {
+ for (int32 x = -12; x <= 12; x++) {
+ int32 dist = y * y + x * x;
+ if (dist > 144)
+ continue;
+ int32 destPitch = surface.pitch;
+ uint8 *curRow = (uint8 *)surface.pixels + (posY + y) * destPitch + (posX + x);
+ int32 lerp = (512 + intSqrt[dist] * 256 / 12);
+ *curRow = tempBuffer[(y*lerp/1024+12) * 25 + x*lerp/1024 + 12];
+ }
+ }
+}
+
+void ToonEngine::copyToVirtualScreen(bool updateScreen) {
+ // render cursor last
+ if (!_gameState->_mouseHidden) {
+ if (_cursorAnimationInstance->getFrame() == 7) // magnifier icon needs a special effect
+ doMagnifierEffect();
+ _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();
+
+ // do we need to load directly a game?
+ bool loadedGame = false;
+ int32 slot = ConfMan.getInt("save_slot");
+ if (slot > -1) {
+ loadedGame = loadGame(slot);
+ }
+
+ if (!loadedGame) {
+
+ // play producer intro
+ getMoviePlayer()->play("MISC/VIELOGOM.SMK", 0x10);
+
+ // show mainmenu
+ 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;
+ _showConversationText = true;
+ _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 = 4;
+ 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(bool waitCharacterToTalk) {
+ int32 elapsedTime = _system->getMillis() - _oldTimer2;
+ _oldTimer2 = _system->getMillis();
+ _oldTimer = _oldTimer2;
+
+ if (!_audioManager->voiceStillPlaying() && !waitCharacterToTalk) {
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+
+ updateCharacters(elapsedTime);
+ updateAnimationSceneScripts(elapsedTime);
+ updateTimer(elapsedTime);
+ _animationManager->update(elapsedTime);
+ _audioManager->updateAmbientSFX();
+ render();
+
+
+}
+
+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 && !_sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) {
+ _animationSceneScriptRunFlag = true;
+
+ while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) {
+ if (!_script->run(&_sceneAnimationScripts[_lastProcessedSceneScript]._state))
+ _animationSceneScriptRunFlag = false;
+
+ //waitForScriptStep();
+
+ if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen || _sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation)
+ 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 = 0;
+
+ if (!forGameLoad) {
+ _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;
+ _mouseButton = 0;
+ _lastMouseButton = 0x3;
+
+
+ // 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;
+ _sceneAnimationScripts[i]._frozenForConversation = 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();
+
+ state()->_mouseHidden = false;
+
+ 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();
+
+ }
+}
+
+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, int 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) == 0 && (_mouseButton & 0x1) == 1)
+ leftButton = true;
+ if ((_lastMouseButton & 0x2) == 0 && (_mouseButton & 0x2) == 2)
+ 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 = 0;
+ return;
+ } else {
+ showInventory();
+ }
+ return;
+ }
+ }
+
+ // with inventory
+ if (rightButton && _gameState->_mouseState >= 0) {
+ addItemToInventory(_gameState->_mouseState);
+ setCursor(0, false, 0, 0);
+ _currentHotspotItem = 0;
+ 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;
+ }
+
+ _audioManager->killAllAmbientSFX();
+ _audioManager->stopAllSfxs();
+ _audioManager->stopCurrentVoice();
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ _currentTextLineCharacterId = 0;
+
+ 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()) {
+ if (_currentTextLineCharacterId == 0 || _currentTextLineCharacterId == 1) {
+ // Drew or Flux 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) {
+ Character *character = getCharacterById(talkerId);
+ if (character)
+ character->setTalking(true);
+
+ playTalkAnimOnCharacter(talkerAnimId, talkerId, true);
+
+ // set once more the values, they may have been overwritten when the engine
+ // waits for the character to be ready.
+ _currentTextLine = myLine;
+ _currentTextLineCharacterId = talkerId;
+ _currentTextLineId = dialogid;
+
+
+ } 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;
+
+ Character *character = getCharacterById(talkerId);
+ if (character)
+ character->setTalking(false);
+ }
+
+
+ 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 (READ_LE_INT16(i) >= 0) {
+ if (READ_LE_INT16(i) < 100) {
+ if (_gameState->_exitConversation == false) {
+ characterTalk(READ_LE_INT16(i + 1));
+ }
+ } else {
+ runConversationCommand(&i);
+ }
+ i += 2;
+ }
+
+ int16 command = READ_LE_INT16(i);
+ int16 value = READ_LE_INT16(i + 1);
+
+ if (command == -1) {
+ v2->_data2 = 0;
+ } else if (command == -2) {
+ v2->_data4 = (char *)_conversationData + value;
+ v2->_data3 = READ_LE_INT16(v2->_data4);
+ } else if (command == -3) {
+ v2->_data2 = 2;
+ v2->_data4 = (char *)_conversationData + value;
+ v2->_data3 = READ_LE_INT16(v2->_data4);
+ }
+
+ int16 *v7 = i + 2;
+ int16 v8 = READ_LE_INT16(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 = READ_LE_INT16(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 = READ_LE_INT16(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[30] & 1))
+ return 0;
+ break;
+ case 0x3e9:
+ if (!(_gameState->_gameGlobalData[30] & 2))
+ return 0;
+ break;
+ case 0x3ea:
+ if (!(_gameState->_gameGlobalData[30] & 4))
+ return 0;
+ break;
+ case 0x3eb:
+ if (!(_gameState->_gameGlobalData[30] & 8))
+ return 0;
+ break;
+ case 0x3ec:
+ if (!(_gameState->_gameGlobalData[30] & 16))
+ return 0;
+ break;
+ case 0x3ed:
+ if (!(_gameState->_gameGlobalData[30] & 32))
+ return 0;
+ break;
+ case 0x3ee:
+ if (!(_gameState->_gameGlobalData[30] & 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;
+}
+
+int32 ToonEngine::runConversationCommand(int16 **command) {
+
+ int16 *v5 = *command;
+
+ int v2 = READ_LE_INT16(v5);
+ int v4 = READ_LE_INT16(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 = READ_LE_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 = 0x3;
+ 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;
+ _mouseButton = 0;
+ _lastMouseButton = 0x3;
+
+ 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 && _showConversationText) {
+ _fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
+ _fontRenderer->setFont(_fontToon);
+ _fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, Common::String(_currentTextLine), 0);
+ }
+}
+
+void ToonEngine::pauseEngineIntern(bool pause) {
+
+ Engine::pauseEngineIntern(pause);
+
+ static int32 pauseStart = 0;
+ if (pause) {
+ pauseStart = _system->getMillis();
+
+ } else {
+ _oldTimer = _system->getMillis();
+ _oldTimer2 = _oldTimer;
+
+ int32 diff = _oldTimer - pauseStart;
+
+ // we have to add the difference between the start and the current time
+ // to all "timer based" values.
+ for (int32 i = 0; i < _gameState->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
+ _sceneAnimationScripts[i]._lastTimer += diff;
+ }
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i]) {
+ _characters[i]->updateTimers(diff);
+ }
+ }
+
+ _gameState->_timerTimeout[0] += diff;
+ _gameState->_timerTimeout[1] += diff;
+ }
+}
+
+bool ToonEngine::canSaveGameStateCurrently() {
+ return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
+}
+
+bool ToonEngine::canLoadGameStateCurrently() {
+ return !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
+}
+
+Common::String ToonEngine::getSavegameName(int nr) {
+ return _targetName + Common::String::printf(".%03d", nr);
+}
+
+bool ToonEngine::saveGame(int32 slot, Common::String saveGameDesc) {
+ 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;
+ if (!saveGameDesc.empty()) {
+ savegameDescription = saveGameDesc;
+ } else {
+ 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();
+ _sceneAnimationScripts[i]._frozenForConversation = false;
+ int32 oldTimer = loadFile->readSint32BE();
+ _sceneAnimationScripts[i]._lastTimer = MAX<int32>(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);
+ }
+ }
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/toon.h b/engines/toon/toon.h
new file mode 100644
index 0000000000..299cb403b1
--- /dev/null
+++ b/engines/toon/toon.h
@@ -0,0 +1,424 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along 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 "common/error.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
+
+/**
+ * This is the namespace of the Toon engine.
+ *
+ * Status of this engine: ???
+ *
+ * Games using this engine:
+ * - Toonstruck
+ */
+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(bool waitCharacterToTalk = false);
+ 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, Common::String saveGameDesc);
+ 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();
+ void doMagnifierEffect();
+
+ bool canSaveGameStateCurrently();
+ bool canLoadGameStateCurrently();
+ void pauseEngineIntern(bool pause);
+
+ 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;
+ }
+
+ int32 getCurrentCharacterTalking() {
+ return _currentTextLineCharacterId;
+ }
+
+ 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;
+ }
+
+ Common::Error saveGameState(int slot, const char *desc) {
+
+ return (saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError);
+ }
+
+ Common::Error loadGameState(int slot) {
+ return (loadGame(slot) ? Common::kReadingFailed : Common::kNoError);
+ }
+
+ bool hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+ }
+
+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;
+ bool _showConversationText;
+};
+
+} // 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;
}
diff --git a/engines/touche/touche.h b/engines/touche/touche.h
index 33ca81ed4f..0081cadbae 100644
--- a/engines/touche/touche.h
+++ b/engines/touche/touche.h
@@ -42,8 +42,8 @@
*
* Status of this engine: ???
*
- * Supported games:
- * - ???
+ * Games using this engine:
+ * - Touche: The Adventures of the Fifth Musketeer
*/
namespace Touche {
diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h
index 86f5843e77..de7042386b 100644
--- a/engines/tucker/tucker.h
+++ b/engines/tucker/tucker.h
@@ -44,7 +44,7 @@
*
* Status of this engine: Complete
*
- * Supported games:
+ * Games using this engine:
* - Bud Tucker in Double Trouble
*/
namespace Tucker {
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp
index 725d7f173b..0237712f13 100644
--- a/graphics/VectorRenderer.cpp
+++ b/graphics/VectorRenderer.cpp
@@ -103,7 +103,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
break;
default:
- error("Vertical alignment in horizontal data.");
+ error("Vertical alignment in horizontal data");
}
} else {
in_x = area.left;
@@ -132,7 +132,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect &
break;
default:
- error("Horizontal alignment in vertical data.");
+ error("Horizontal alignment in vertical data");
}
} else {
in_y = area.top;
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp
index 966c56cf4e..0ee13033af 100644
--- a/graphics/VectorRendererSpec.cpp
+++ b/graphics/VectorRendererSpec.cpp
@@ -255,7 +255,7 @@ void VectorRendererSpec<PixelType>::
fillSurface() {
byte *ptr = (byte *)_activeSurface->getBasePtr(0, 0);
- int h = _activeSurface->h ;
+ int h = _activeSurface->h;
int pitch = _activeSurface->pitch;
if (Base::_fillMode == kFillBackground) {
diff --git a/graphics/colormasks.h b/graphics/colormasks.h
index 1ab78fccc5..824d980ca3 100644
--- a/graphics/colormasks.h
+++ b/graphics/colormasks.h
@@ -168,6 +168,30 @@ struct ColorMasks<1555> {
};
template<>
+struct ColorMasks<5551> {
+ enum {
+ kBytesPerPixel = 2,
+
+ kAlphaBits = 1,
+ kRedBits = 5,
+ kGreenBits = 5,
+ kBlueBits = 5,
+
+ kAlphaShift = 0,
+ kRedShift = kGreenBits+kBlueBits+kAlphaBits,
+ kGreenShift = kBlueBits+kAlphaBits,
+ kBlueShift = kAlphaBits,
+
+ kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift,
+ kRedMask = ((1 << kRedBits) - 1) << kRedShift,
+ kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift,
+ kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift,
+
+ kRedBlueMask = kRedMask | kBlueMask
+ };
+};
+
+template<>
struct ColorMasks<4444> {
enum {
kBytesPerPixel = 2,
diff --git a/graphics/jpeg.cpp b/graphics/jpeg.cpp
index 1c7ad64646..fbc81b5db2 100644
--- a/graphics/jpeg.cpp
+++ b/graphics/jpeg.cpp
@@ -198,7 +198,7 @@ bool JPEG::readJFIF() {
byte majorVersion = _str->readByte();
byte minorVersion = _str->readByte();
if(majorVersion != 1 || minorVersion != 1)
- warning("JPEG::readJFIF() Non-v1.1 JPEGs may not be handled correctly!");
+ warning("JPEG::readJFIF() Non-v1.1 JPEGs may not be handled correctly");
/* byte densityUnits = */ _str->readByte();
/* uint16 xDensity = */ _str->readUint16BE();
/* uint16 yDensity = */ _str->readUint16BE();
diff --git a/graphics/pict.cpp b/graphics/pict.cpp
index f0dd7bbc6f..ebf643439b 100644
--- a/graphics/pict.cpp
+++ b/graphics/pict.cpp
@@ -48,6 +48,8 @@ PictDecoder::~PictDecoder() {
Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) {
assert(stream);
+ _outputSurface = 0;
+
uint16 fileSize = stream->readUint16BE();
// If we have no file size here, we probably have a PICT from a file
@@ -61,9 +63,6 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale
_imageRect.bottom = stream->readUint16BE();
_imageRect.right = stream->readUint16BE();
_imageRect.debugPrint(0, "PICT Rect:");
-
- Graphics::Surface *image = new Graphics::Surface();
- image->create(_imageRect.width(), _imageRect.height(), _pixelFormat.bytesPerPixel);
_isPaletted = false;
// NOTE: This is only a subset of the full PICT format.
@@ -96,10 +95,10 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale
} else if (opcode == 0x001E) { // DefHilite
// Ignore, Contains no Data
} else if (opcode == 0x0098) { // PackBitsRect
- decodeDirectBitsRect(stream, image, true);
+ decodeDirectBitsRect(stream, _imageRect.width(), _imageRect.height(), true);
_isPaletted = true;
} else if (opcode == 0x009A) { // DirectBitsRect
- decodeDirectBitsRect(stream, image, false);
+ decodeDirectBitsRect(stream, _imageRect.width(), _imageRect.height(), false);
} else if (opcode == 0x00A1) { // LongComment
stream->readUint16BE();
uint16 dataSize = stream->readUint16BE();
@@ -119,7 +118,7 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale
origResRect.right = stream->readUint16BE();
stream->readUint32BE(); // Reserved
} else if (opcode == 0x8200) { // CompressedQuickTime
- decodeCompressedQuickTime(stream, image);
+ decodeCompressedQuickTime(stream);
break;
} else {
warning("Unknown PICT opcode %04x", opcode);
@@ -130,7 +129,7 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale
if (palette && _isPaletted)
memcpy(palette, _palette, 256 * 4);
- return image;
+ return _outputSurface;
}
PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr) {
@@ -163,7 +162,7 @@ struct DirectBitsRectData {
uint16 mode;
};
-void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette) {
+void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, uint16 width, uint16 height, bool hasPalette) {
static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
// Clear the palette
@@ -197,11 +196,18 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa
directBitsData.dstRect.right = stream->readUint16BE();
directBitsData.mode = stream->readUint16BE();
- if (directBitsData.pixMap.pixelSize != 8 && directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32)
- error("Unhandled DirectBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize);
+ byte bytesPerPixel = 0;
- byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 32) ? 3 : directBitsData.pixMap.pixelSize / 8;
- byte *buffer = new byte[image->w * image->h * bytesPerPixel];
+ if (directBitsData.pixMap.pixelSize <= 8)
+ bytesPerPixel = 1;
+ else if (directBitsData.pixMap.pixelSize == 32)
+ bytesPerPixel = 3;
+ else
+ bytesPerPixel = directBitsData.pixMap.pixelSize / 8;
+
+ _outputSurface = new Graphics::Surface();
+ _outputSurface->create(width, height, (bytesPerPixel == 1) ? 1 : _pixelFormat.bytesPerPixel);
+ byte *buffer = new byte[width * height * bytesPerPixel];
// Read in amount of data per row
for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) {
@@ -217,37 +223,37 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa
error("Unpacked DirectBitsRect data (not padded)");
} else if (directBitsData.pixMap.packType == 0 || directBitsData.pixMap.packType > 2) { // Packed
uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte();
- decodeDirectBitsLine(buffer + i * image->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), bytesPerPixel);
+ decodeDirectBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), directBitsData.pixMap.pixelSize, bytesPerPixel);
}
}
if (bytesPerPixel == 1) {
// Just copy to the image
- memcpy(image->pixels, buffer, image->w * image->h);
+ memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);
} else if (bytesPerPixel == 2) {
// Convert from 16-bit to whatever surface we need
- for (uint16 y = 0; y < image->h; y++) {
- for (uint16 x = 0; x < image->w; x++) {
+ for (uint16 y = 0; y < _outputSurface->h; y++) {
+ for (uint16 x = 0; x < _outputSurface->w; x++) {
byte r = 0, g = 0, b = 0;
- uint32 color = READ_BE_UINT16(buffer + (y * image->w + x) * bytesPerPixel);
+ uint32 color = READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * bytesPerPixel);
directBitsFormat16.colorToRGB(color, r, g, b);
if (_pixelFormat.bytesPerPixel == 2)
- *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
else
- *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
}
}
} else {
// Convert from 24-bit (planar!) to whatever surface we need
- for (uint16 y = 0; y < image->h; y++) {
- for (uint16 x = 0; x < image->w; x++) {
- byte r = *(buffer + y * image->w * 3 + x);
- byte g = *(buffer + y * image->w * 3 + image->w + x);
- byte b = *(buffer + y * image->w * 3 + image->w * 2 + x);
+ for (uint16 y = 0; y < _outputSurface->h; y++) {
+ for (uint16 x = 0; x < _outputSurface->w; x++) {
+ byte r = *(buffer + y * _outputSurface->w * 3 + x);
+ byte g = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w + x);
+ byte b = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x);
if (_pixelFormat.bytesPerPixel == 2)
- *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
else
- *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
}
}
}
@@ -255,7 +261,7 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa
delete[] buffer;
}
-void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) {
+void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) {
uint32 dataDecoded = 0;
byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1;
@@ -270,14 +276,17 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl
if (bytesPerDecode == 2) {
WRITE_BE_UINT16(out, value);
out += 2;
- } else
- *out++ = value;
+ } else {
+ outputPixelBuffer(out, value, bitsPerPixel);
+ }
}
dataDecoded += runSize * bytesPerDecode;
} else {
uint32 runSize = (op + 1) * bytesPerDecode;
+
for (uint32 i = 0; i < runSize; i++)
- *out++ = data->readByte();
+ outputPixelBuffer(out, data->readByte(), bitsPerPixel);
+
dataDecoded += runSize;
}
}
@@ -292,12 +301,31 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl
delete data;
}
+void PictDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
+ switch (bitsPerPixel) {
+ case 1:
+ for (int i = 7; i >= 0; i--)
+ *out++ = (value >> i) & 1;
+ break;
+ case 2:
+ for (int i = 6; i >= 0; i -= 2)
+ *out++ = (value >> i) & 3;
+ break;
+ case 4:
+ *out++ = (value >> 4) & 0xf;
+ *out++ = value & 0xf;
+ break;
+ default:
+ *out++ = value;
+ }
+}
+
// Compressed QuickTime details can be found here:
// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
// I'm just ignoring that because Myst ME uses none of that extra stuff. The offset is always the same.
-void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) {
+void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream) {
uint32 dataSize = stream->readUint32BE();
uint32 startPos = stream->pos();
@@ -310,23 +338,21 @@ void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream,
Surface *uComponent = _jpeg->getComponent(2);
Surface *vComponent = _jpeg->getComponent(3);
- Surface jpegImage;
- jpegImage.create(yComponent->w, yComponent->h, _pixelFormat.bytesPerPixel);
+ _outputSurface = new Graphics::Surface();
+ _outputSurface->create(yComponent->w, yComponent->h, _pixelFormat.bytesPerPixel);
- for (uint16 i = 0; i < jpegImage.h; i++) {
- for (uint16 j = 0; j < jpegImage.w; j++) {
+ for (uint16 i = 0; i < _outputSurface->h; i++) {
+ for (uint16 j = 0; j < _outputSurface->w; j++) {
byte r = 0, g = 0, b = 0;
YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b);
if (_pixelFormat.bytesPerPixel == 2)
- *((uint16 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint16 *)_outputSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
else
- *((uint32 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
+ *((uint32 *)_outputSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b);
}
}
- image->copyFrom(jpegImage);
stream->seek(startPos + dataSize);
- jpegImage.free();
delete jpegStream;
}
diff --git a/graphics/pict.h b/graphics/pict.h
index 12681f6128..a9ea170292 100644
--- a/graphics/pict.h
+++ b/graphics/pict.h
@@ -72,10 +72,12 @@ private:
JPEG *_jpeg;
byte _palette[256 * 4];
bool _isPaletted;
+ Graphics::Surface *_outputSurface;
- void decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette);
- void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel);
- void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Surface *image);
+ void decodeDirectBitsRect(Common::SeekableReadStream *stream, uint16 width, uint16 height, bool hasPalette);
+ void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel);
+ void decodeCompressedQuickTime(Common::SeekableReadStream *stream);
+ void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
};
} // End of namespace Graphics
diff --git a/graphics/sjis.cpp b/graphics/sjis.cpp
index af286346ca..b9d0d8332f 100644
--- a/graphics/sjis.cpp
+++ b/graphics/sjis.cpp
@@ -53,7 +53,7 @@ FontSJIS *FontSJIS::createFont(const Common::Platform platform) {
}
template<typename Color>
-void FontSJISBase::blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c) const {
+void FontSJISBase::blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c1, Color c2) const {
for (int y = 0; y < h; ++y) {
Color *d = (Color *)dst;
dst += pitch;
@@ -63,8 +63,13 @@ void FontSJISBase::blitCharacter(const uint8 *glyph, const int w, const int h, u
if (!(x % 8))
mask = *glyph++;
- if (mask & 0x80)
- *d = c;
+ if (mask & 0x80) {
+ *d = c1;
+ if (_drawMode == kShadowMode || _drawMode == kFMTownsShadowMode)
+ d[1] = d[pitch] = c2;
+ if (_drawMode == kShadowMode)
+ d[pitch + 1] = c2;
+ }
++d;
mask <<= 1;
}
@@ -103,6 +108,36 @@ void FontSJISBase::createOutline(uint8 *outline, const uint8 *glyph, const int w
}
}
+#ifndef DISABLE_FLIPPED_MODE
+const uint8 *FontSJISBase::flipCharacter(const uint8 *glyph, const int w) const {
+ static const uint8 flipData[] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xC7, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x97, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+ };
+
+ for (int i = 0; i < w; i++) {
+ _tempGlyph[i] = flipData[glyph[(w * 2 - 1) - i]];
+ _tempGlyph[(w * 2 - 1) - i] = flipData[glyph[i]];
+ }
+
+ return _tempGlyph;
+}
+#endif
+
void FontSJISBase::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, uint32 c2) const {
const uint8 *glyphSource = 0;
int width = 0, height = 0;
@@ -121,25 +156,30 @@ void FontSJISBase::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1,
return;
}
+#ifndef DISABLE_FLIPPED_MODE
+ if (_flippedMode)
+ glyphSource = flipCharacter(glyphSource, width);
+#endif
+
uint8 outline[18 * 18];
- if (_outlineEnabled) {
+ if (_drawMode == kOutlineMode) {
memset(outline, 0, sizeof(outline));
createOutline(outline, glyphSource, width, height);
}
if (bpp == 1) {
- if (_outlineEnabled) {
+ if (_drawMode == kOutlineMode) {
blitCharacter<uint8>(outline, width + 2, height + 2, (uint8 *)dst, pitch, c2);
blitCharacter<uint8>(glyphSource, width, height, (uint8 *)dst + pitch + 1, pitch, c1);
} else {
- blitCharacter<uint8>(glyphSource, width, height, (uint8 *)dst, pitch, c1);
+ blitCharacter<uint8>(glyphSource, width, height, (uint8 *)dst, pitch, c1, c2);
}
} else if (bpp == 2) {
- if (_outlineEnabled) {
+ if (_drawMode == kOutlineMode) {
blitCharacter<uint16>(outline, width + 2, height + 2, (uint8 *)dst, pitch, c2);
blitCharacter<uint16>(glyphSource, width, height, (uint8 *)dst + pitch + 2, pitch, c1);
} else {
- blitCharacter<uint16>(glyphSource, width, height, (uint8 *)dst, pitch, c1);
+ blitCharacter<uint16>(glyphSource, width, height, (uint8 *)dst, pitch, c1, c2);
}
} else {
error("FontSJISBase::drawChar: unsupported bpp: %d", bpp);
@@ -148,7 +188,7 @@ void FontSJISBase::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1,
uint FontSJISBase::getCharWidth(uint16 ch) const {
if (is8x16(ch))
- return _outlineEnabled ? 10 : 8;
+ return (_drawMode == kOutlineMode) ? 10 : (_drawMode == kDefaultMode ? 8 : 9);
else
return getMaxFontWidth();
}
diff --git a/graphics/sjis.h b/graphics/sjis.h
index e0b760fc78..4ade2f5278 100644
--- a/graphics/sjis.h
+++ b/graphics/sjis.h
@@ -28,7 +28,7 @@
// for dynamic engine plugins.
// If you plan to use this code in another engine, you will have
// to add the proper define check here.
-#if !(defined(ENABLE_KYRA) || defined(ENABLE_SCI) || defined(DYNAMIC_MODULES))
+#if !(defined(ENABLE_KYRA) || defined(ENABLE_SCI) || defined(ENABLE_SCUMM) || defined(DYNAMIC_MODULES))
// If neither of the above mentioned is enabled, do not include the SJIS code.
@@ -37,6 +37,14 @@
#ifndef GRAPHICS_SJIS_H
#define GRAPHICS_SJIS_H
+#ifdef __DS__
+/* This disables the flipped mode which is used in FM-Towns versions
+ * of Monkey Island 1 (and maybe other SCUMM 5 games). These are not supported
+ * on the DS, so it makes sense to have a corresponding setting here.
+ */
+#define DISABLE_FLIPPED_MODE
+#endif
+
#include "common/scummsys.h"
#include "common/stream.h"
#include "common/util.h"
@@ -71,12 +79,24 @@ public:
virtual bool loadData() = 0;
/**
- * Enable outline drawing.
+ * Enable drawing with outline or shadow.
*
* After changing outline state, getFontHeight and getMaxFontWidth / getCharWidth might return
* different values!
*/
- virtual void enableOutline(bool enable) {}
+ enum DrawingMode {
+ kDefaultMode,
+ kOutlineMode,
+ kShadowMode,
+ kFMTownsShadowMode
+ };
+
+ virtual void setDrawingMode(DrawingMode mode) {}
+
+ /**
+ * Enable flipped character drawing (e.g. in the MI1 circus scene after Guybrush gets shot out of the cannon).
+ */
+ virtual void toggleFlippedMode(bool enable) {}
/**
* Returns the height of the font.
@@ -122,22 +142,33 @@ public:
*/
class FontSJISBase : public FontSJIS {
public:
- FontSJISBase() : _outlineEnabled(false) {}
+ FontSJISBase() : _drawMode(kDefaultMode), _flippedMode(false) {}
+
+ void setDrawingMode(DrawingMode mode) { _drawMode = mode; }
- void enableOutline(bool enable) { _outlineEnabled = enable; }
+ void toggleFlippedMode(bool enable) { _flippedMode = enable; }
- uint getFontHeight() const { return _outlineEnabled ? 18 : 16; }
- uint getMaxFontWidth() const { return _outlineEnabled ? 18 : 16; }
+ uint getFontHeight() const { return (_drawMode == kOutlineMode) ? 18 : (_drawMode == kDefaultMode ? 16 : 17); }
+
+ uint getMaxFontWidth() const { return (_drawMode == kOutlineMode) ? 18 : (_drawMode == kDefaultMode ? 16 : 17); }
uint getCharWidth(uint16 ch) const;
void drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, uint32 c2) const;
private:
template<typename Color>
- void blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c) const;
+ void blitCharacter(const uint8 *glyph, const int w, const int h, uint8 *dst, int pitch, Color c1, Color c2 = 0) const;
void createOutline(uint8 *outline, const uint8 *glyph, const int w, const int h) const;
+
+#ifndef DISABLE_FLIPPED_MODE
+ // This is used in the FM-Towns version of Monkey Island 1
+ // when Guybrush gets shot out of the cannon in the circus tent.
+ const uint8 *flipCharacter(const uint8 *glyph, const int w) const;
+ mutable uint8 _tempGlyph[32];
+#endif
protected:
- bool _outlineEnabled;
+ DrawingMode _drawMode;
+ bool _flippedMode;
bool is8x16(uint16 ch) const;
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 137ef29a4e..f06c24c2cd 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -24,6 +24,7 @@
#include "common/algorithm.h"
#include "common/util.h"
+#include "common/endian.h"
#include "graphics/primitives.h"
#include "graphics/surface.h"
@@ -179,65 +180,72 @@ void Surface::frameRect(const Common::Rect &r, uint32 color) {
vLine(r.right-1, r.top, r.bottom-1, color);
}
-// FIXME: LordHoto asks why is this in Surface, since this
-// just supports 8bpp surfaces. Looks like someone wants
-// to subclass Surface to add this or it should be extended
-// to support 16bpp (or marked as just working for 8bpp
-// surfaces).
void Surface::move(int dx, int dy, int height) {
- // This function currently just works with 8bpp surfaces
- assert(bytesPerPixel == 1);
-
// Short circuit check - do we have to do anything anyway?
if ((dx == 0 && dy == 0) || height <= 0)
return;
+ if (bytesPerPixel != 1 && bytesPerPixel != 2)
+ error("Surface::move: bytesPerPixel must be 1 or 2");
+
byte *src, *dst;
int x, y;
// vertical movement
if (dy > 0) {
// move down - copy from bottom to top
- dst = (byte *)pixels + (height - 1) * w;
- src = dst - dy * w;
+ dst = (byte *)pixels + (height - 1) * pitch;
+ src = dst - dy * pitch;
for (y = dy; y < height; y++) {
- memcpy(dst, src, w);
- src -= w;
- dst -= w;
+ memcpy(dst, src, pitch);
+ src -= pitch;
+ dst -= pitch;
}
} else if (dy < 0) {
// move up - copy from top to bottom
dst = (byte *)pixels;
- src = dst - dy * w;
+ src = dst - dy * pitch;
for (y = -dy; y < height; y++) {
- memcpy(dst, src, w);
- src += w;
- dst += w;
+ memcpy(dst, src, pitch);
+ src += pitch;
+ dst += pitch;
}
}
// horizontal movement
if (dx > 0) {
// move right - copy from right to left
- dst = (byte *)pixels + (w - 1);
- src = dst - dx;
+ dst = (byte *)pixels + (pitch - bytesPerPixel);
+ src = dst - (dx * bytesPerPixel);
for (y = 0; y < height; y++) {
for (x = dx; x < w; x++) {
- *dst-- = *src--;
+ if (bytesPerPixel == 1) {
+ *dst-- = *src--;
+ } else if (bytesPerPixel == 2) {
+ *(uint16 *)dst = *(const uint16 *)src;
+ src -= 2;
+ dst -= 2;
+ }
}
- src += w + (w - dx);
- dst += w + (w - dx);
+ src += pitch + (pitch - dx * bytesPerPixel);
+ dst += pitch + (pitch - dx * bytesPerPixel);
}
} else if (dx < 0) {
// move left - copy from left to right
dst = (byte *)pixels;
- src = dst - dx;
+ src = dst - (dx * bytesPerPixel);
for (y = 0; y < height; y++) {
for (x = -dx; x < w; x++) {
- *dst++ = *src++;
+ if (bytesPerPixel == 1) {
+ *dst++ = *src++;
+ } else if (bytesPerPixel == 2) {
+ *(uint16 *)dst = *(const uint16 *)src;
+ src += 2;
+ dst += 2;
+ }
}
- src += w - (w + dx);
- dst += w - (w + dx);
+ src += pitch - (pitch + dx * bytesPerPixel);
+ dst += pitch - (pitch + dx * bytesPerPixel);
}
}
}
diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp
index 4973cb3eb0..ceca89b8ee 100644
--- a/graphics/video/avi_decoder.cpp
+++ b/graphics/video/avi_decoder.cpp
@@ -42,6 +42,20 @@
namespace Graphics {
+/*
+static byte char2num(char c) {
+ return (c >= 48 && c <= 57) ? c - 48 : 0;
+}
+
+static byte getStreamNum(uint32 tag) {
+ return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16));
+}
+*/
+
+static uint16 getStreamType(uint32 tag) {
+ return tag & 0xffff;
+}
+
AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) {
_soundType = soundType;
@@ -418,16 +432,4 @@ Audio::QueuingAudioStream *AviDecoder::createAudioStream() {
return NULL;
}
-byte AviDecoder::char2num(char c) {
- return (c >= 48 && c <= 57) ? c - 48 : 0;
-}
-
-byte AviDecoder::getStreamNum(uint32 tag) {
- return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16));
-}
-
-uint16 AviDecoder::getStreamType(uint32 tag) {
- return tag & 0xffff;
-}
-
} // End of namespace Graphics
diff --git a/graphics/video/avi_decoder.h b/graphics/video/avi_decoder.h
index 72cf2d7ef5..68795149c0 100644
--- a/graphics/video/avi_decoder.h
+++ b/graphics/video/avi_decoder.h
@@ -221,11 +221,6 @@ private:
Audio::SoundHandle *_audHandle;
Audio::QueuingAudioStream *_audStream;
Audio::QueuingAudioStream *createAudioStream();
-
- // Helper functions
- static byte char2num(char c);
- static byte getStreamNum(uint32 tag);
- static uint16 getStreamType(uint32 tag);
};
} // End of namespace Graphics
diff --git a/graphics/video/codecs/indeo3.cpp b/graphics/video/codecs/indeo3.cpp
index c99afbd0d3..443340de2f 100644
--- a/graphics/video/codecs/indeo3.cpp
+++ b/graphics/video/codecs/indeo3.cpp
@@ -269,31 +269,83 @@ Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) {
const byte *srcU = _cur_frame->Ubuf;
const byte *srcV = _cur_frame->Vbuf;
byte *dest = (byte *)_surface->pixels;
+
+ const byte *srcUP = srcU;
+ const byte *srcVP = srcV;
+ const byte *srcUN = srcU + chromaWidth;
+ const byte *srcVN = srcV + chromaWidth;
+
+ uint32 scaleWidth = _surface->w / fWidth;
+ uint32 scaleHeight = _surface->h / fHeight;
+
for (uint32 y = 0; y < fHeight; y++) {
byte *rowDest = dest;
- for (uint32 x = 0; x < fWidth; x++, rowDest += _surface->bytesPerPixel) {
- const byte cY = srcY[x];
- const byte cU = srcU[x >> 2];
- const byte cV = srcV[x >> 2];
+ for (uint32 sH = 0; sH < scaleHeight; sH++) {
+ for (uint32 x = 0; x < fWidth; x++) {
+ uint32 xP = MAX<int32>((x >> 2) - 1, 0);
+ uint32 xN = MIN<int32>((x >> 2) + 1, chromaWidth - 1);
+
+ byte cY = srcY[x];
+ byte cU = srcU[x >> 2];
+ byte cV = srcV[x >> 2];
+
+ if (((x % 4) == 0) && ((y % 4) == 0)) {
+ cU = (((uint32) cU) + ((uint32) srcUP[xP])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVP[xP])) / 2;
+ } else if (((x % 4) == 3) && ((y % 4) == 0)) {
+ cU = (((uint32) cU) + ((uint32) srcUP[xN])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVP[xN])) / 2;
+ } else if (((x % 4) == 0) && ((y % 4) == 3)) {
+ cU = (((uint32) cU) + ((uint32) srcUN[xP])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVN[xP])) / 2;
+ } else if (((x % 4) == 3) && ((y % 4) == 3)) {
+ cU = (((uint32) cU) + ((uint32) srcUN[xN])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVN[xN])) / 2;
+ } else if ( (x % 4) == 0) {
+ cU = (((uint32) cU) + ((uint32) srcU[xP])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcV[xP])) / 2;
+ } else if ( (x % 4) == 3) {
+ cU = (((uint32) cU) + ((uint32) srcU[xN])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcV[xN])) / 2;
+ } else if ( (y % 4) == 0) {
+ cU = (((uint32) cU) + ((uint32) srcUP[x >> 2])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVP[x >> 2])) / 2;
+ } else if ( (y % 4) == 3) {
+ cU = (((uint32) cU) + ((uint32) srcUN[x >> 2])) / 2;
+ cV = (((uint32) cV) + ((uint32) srcVN[x >> 2])) / 2;
+ }
+
+ byte r = 0, g = 0, b = 0;
+ YUV2RGB(cY, cU, cV, r, g, b);
- byte r = 0, g = 0, b = 0;
- YUV2RGB(cY, cU, cV, r, g, b);
+ const uint32 color = _pixelFormat.RGBToColor(r, g, b);
- const uint32 color = _pixelFormat.RGBToColor(r, g, b);
+ for (uint32 sW = 0; sW < scaleWidth; sW++, rowDest += _surface->bytesPerPixel) {
+ if (_surface->bytesPerPixel == 1)
+ *((uint8 *)rowDest) = (uint8)color;
+ else if (_surface->bytesPerPixel == 2)
+ *((uint16 *)rowDest) = (uint16)color;
+ }
+ }
- if (_surface->bytesPerPixel == 1)
- *((uint8 *)rowDest) = (uint8)color;
- else if (_surface->bytesPerPixel == 2)
- *((uint16 *)rowDest) = (uint16)color;
+ dest += _surface->pitch;
}
- dest += _surface->pitch;
srcY += fWidth;
if ((y & 3) == 3) {
- srcU += fWidth >> 2;
- srcV += fWidth >> 2;
+ srcU += chromaWidth;
+ srcV += chromaWidth;
+
+ if (y > 0) {
+ srcUP += chromaWidth;
+ srcVP += chromaWidth;
+ }
+ if (y < (fHeight - 4U)) {
+ srcUN += chromaWidth;
+ srcVN += chromaWidth;
+ }
}
}
diff --git a/graphics/video/codecs/qdm2.cpp b/graphics/video/codecs/qdm2.cpp
index 9f151b4ba8..26b18b189b 100644
--- a/graphics/video/codecs/qdm2.cpp
+++ b/graphics/video/codecs/qdm2.cpp
@@ -1350,15 +1350,20 @@ static int getVlc2(GetBitContext *s, int16 (*table)[2], int bits, int maxDepth)
static int allocTable(VLC *vlc, int size, int use_static) {
int index;
+ int16 (*temp)[2] = NULL;
index = vlc->table_size;
vlc->table_size += size;
if (vlc->table_size > vlc->table_allocated) {
if(use_static)
error("QDM2 cant do anything, init_vlc() is used with too little memory");
vlc->table_allocated += (1 << vlc->bits);
- vlc->table = (int16 (*)[2])realloc(vlc->table, sizeof(int16 *) * 2 * vlc->table_allocated);
- if (!vlc->table)
+ temp = (int16 (*)[2])realloc(vlc->table, sizeof(int16 *) * 2 * vlc->table_allocated);
+ if (!temp) {
+ free(vlc->table);
+ vlc->table = NULL;
return -1;
+ }
+ vlc->table = temp;
}
return index;
}
@@ -1775,7 +1780,7 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS
tmp = extraData->readUint32BE();
debug(1, "QDM2Stream::QDM2Stream() extraType: %d", tmp);
if (tmp == MKID_BE('QDMC'))
- warning("QDM2Stream::QDM2Stream() QDMC stream type not supported.");
+ warning("QDM2Stream::QDM2Stream() QDMC stream type not supported");
else if (tmp != MKID_BE('QDM2'))
error("QDM2Stream::QDM2Stream() Unsupported stream type");
@@ -3112,7 +3117,6 @@ void QDM2Stream::qdm2_fft_tone_synthesizer(uint8 sub_packet) {
}
void QDM2Stream::qdm2_calculate_fft(int channel) {
- const float gain = (_channels == 1 && _channels == 2) ? 0.5f : 1.0f;
int i;
_fft.complex[channel][0].re *= 2.0f;
@@ -3122,7 +3126,7 @@ void QDM2Stream::qdm2_calculate_fft(int channel) {
// add samples to output buffer
for (i = 0; i < ((_fftFrameSize + 15) & ~15); i++)
- _outputBuffer[_channels * i + channel] += ((float *) _fft.complex[channel])[i] * gain;
+ _outputBuffer[_channels * i + channel] += ((float *) _fft.complex[channel])[i];
}
/**
diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp
index 37aad8f2c8..cea902e3a0 100644
--- a/graphics/video/coktel_decoder.cpp
+++ b/graphics/video/coktel_decoder.cpp
@@ -399,11 +399,11 @@ void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) {
rect.clip(_surface.w, _surface.h);
- byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left;
+ byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left * _surface.bytesPerPixel;
for (int i = 0; i < rect.height(); i++) {
- memcpy(dst, src, rect.width());
+ memcpy(dst, src, rect.width() * _surface.bytesPerPixel);
- src += srcRect.width();
+ src += srcRect.width() * _surface.bytesPerPixel;
dst += _surface.pitch;
}
}
@@ -581,7 +581,7 @@ uint32 CoktelDecoder::getTimeToNextFrame() const {
// the middle of a long video.
if (!hasSound())
- return Common::Rational(1000, _frameRate).toInt();
+ return (1000 / _frameRate).toInt();
// If there /is/ audio, we do need to keep video and audio
// in sync, though.
@@ -1589,12 +1589,6 @@ bool VMDDecoder::load(Common::SeekableReadStream *stream) {
if (_version & 4)
_bytesPerPixel = handle + 1;
- if (_bytesPerPixel != 1) {
- warning("TODO: _bytesPerPixel = %d", _bytesPerPixel);
- close();
- return false;
- }
-
if (_bytesPerPixel > 3) {
warning("VMDDecoder::load(): Requested %d bytes per pixel (%d, %d, %d)",
_bytesPerPixel, headerLength, handle, _version);
@@ -1696,6 +1690,11 @@ bool VMDDecoder::assessVideoProperties() {
_bytesPerPixel = n;
}
+ if ((_bytesPerPixel > 1) && !_externalCodec) {
+ warning("VMDDecoder::assessVideoProperties(): TODO: Internal _bytesPerPixel == %d", _bytesPerPixel);
+ return false;
+ }
+
if (_hasVideo) {
if ((_frameDataSize == 0) || (_frameDataSize > 1048576))
_frameDataSize = _width * _height + 1000;
@@ -2087,9 +2086,19 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) {
return false;
if (_externalCodec) {
- // TODO
- warning("_external codec");
- return false;
+ if (!_codec)
+ return false;
+
+ Common::MemoryReadStream frameStream(_frameData, _frameDataLen);
+ Surface *codecSurf = _codec->decodeImage(&frameStream);
+ if (!codecSurf)
+ return false;
+
+ rect = Common::Rect(_x, _y, _x + codecSurf->w, _y + codecSurf->h);
+ rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height));
+
+ renderBlockWhole((const byte *) codecSurf->pixels, rect);
+ return true;
}
if (_blitMode > 0) {
@@ -2292,12 +2301,15 @@ byte *VMDDecoder::deDPCM(const byte *data, uint32 &size, int32 init[2]) {
uint32 outSize = size + channels;
int16 *out = (int16 *)malloc(outSize * 2);
- byte *sound = (byte *) out;
+ byte *sound = (byte *)out;
+
+ if (!out)
+ return 0;
int channel = 0;
for (int i = 0; i < channels; i++) {
- *out++ = TO_BE_16(init[channel]);
+ *out++ = TO_BE_16(init[channel]);
channel = (channel + 1) % channels;
}
@@ -2376,6 +2388,9 @@ byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 inde
}
PixelFormat VMDDecoder::getPixelFormat() const {
+ if (_externalCodec && _codec)
+ return _codec->getPixelFormat();
+
return PixelFormat::createFormatCLUT8();
}
@@ -2452,6 +2467,7 @@ Common::MemoryReadStream *VMDDecoder::getEmbeddedFile(const Common::String &file
free(data);
warning("VMDDecoder::getEmbeddedFile(): Couldn't read %d bytes (file \"%s\")",
file->realSize, fileName.c_str());
+ return 0;
}
Common::MemoryReadStream *stream =
diff --git a/graphics/video/flic_decoder.cpp b/graphics/video/flic_decoder.cpp
index 843d3ee093..f55a74f4f3 100644
--- a/graphics/video/flic_decoder.cpp
+++ b/graphics/video/flic_decoder.cpp
@@ -70,7 +70,7 @@ bool FlicDecoder::load(Common::SeekableReadStream *stream) {
}
_fileStream->readUint16LE(); // flags
- // Note: The normal delay is a 32-bit integer (dword), whereas the overriden delay is a 16-bit integer (word)
+ // Note: The normal delay is a 32-bit integer (dword), whereas the overridden delay is a 16-bit integer (word)
// the frame delay is the FLIC "speed", in milliseconds.
_frameRate = Common::Rational(1000, _fileStream->readUint32LE());
@@ -207,7 +207,7 @@ Surface *FlicDecoder::decodeNextFrame() {
// this properly.
chunkCount = _fileStream->readUint16LE();
- // Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword)
+ // Note: The overridden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword)
// the frame delay is the FLIC "speed", in milliseconds.
uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds
if (newFrameDelay > 0)
diff --git a/graphics/video/qt_decoder.cpp b/graphics/video/qt_decoder.cpp
index 470441dab8..8437f0af43 100644
--- a/graphics/video/qt_decoder.cpp
+++ b/graphics/video/qt_decoder.cpp
@@ -67,6 +67,8 @@ QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() {
_numStreams = 0;
_fd = 0;
_scaledSurface = 0;
+ _scaleFactorX = 1;
+ _scaleFactorY = 1;
_dirtyPalette = false;
_resFork = new Common::MacResManager();
@@ -82,14 +84,14 @@ uint16 QuickTimeDecoder::getWidth() const {
if (_videoStreamIndex < 0)
return 0;
- return _streams[_videoStreamIndex]->width / getScaleMode();
+ return (Common::Rational(_streams[_videoStreamIndex]->width) / getScaleFactorX()).toInt();
}
uint16 QuickTimeDecoder::getHeight() const {
if (_videoStreamIndex < 0)
return 0;
- return _streams[_videoStreamIndex]->height / getScaleMode();
+ return (Common::Rational(_streams[_videoStreamIndex]->height) / getScaleFactorY()).toInt();
}
uint32 QuickTimeDecoder::getFrameCount() const {
@@ -113,11 +115,18 @@ uint32 QuickTimeDecoder::getCodecTag() {
return _streams[_videoStreamIndex]->codec_tag;
}
-ScaleMode QuickTimeDecoder::getScaleMode() const {
+Common::Rational QuickTimeDecoder::getScaleFactorX() const {
if (_videoStreamIndex < 0)
- return kScaleNormal;
+ return 1;
- return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode);
+ return (_scaleFactorX * _streams[_videoStreamIndex]->scaleFactorX);
+}
+
+Common::Rational QuickTimeDecoder::getScaleFactorY() const {
+ if (_videoStreamIndex < 0)
+ return 1;
+
+ return (_scaleFactorY * _streams[_videoStreamIndex]->scaleFactorY);
}
uint32 QuickTimeDecoder::getFrameDuration() {
@@ -231,14 +240,14 @@ Surface *QuickTimeDecoder::decodeNextFrame() {
}
Surface *QuickTimeDecoder::scaleSurface(Surface *frame) {
- if (getScaleMode() == kScaleNormal)
+ if (getScaleFactorX() == 1 && getScaleFactorY() == 1)
return frame;
assert(_scaledSurface);
- for (uint32 j = 0; j < _scaledSurface->h; j++)
- for (uint32 k = 0; k < _scaledSurface->w; k++)
- memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel);
+ for (int32 j = 0; j < _scaledSurface->h; j++)
+ for (int32 k = 0; k < _scaledSurface->w; k++)
+ memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr((k * getScaleFactorX()).toInt() , (j * getScaleFactorY()).toInt()), frame->bytesPerPixel);
return _scaledSurface;
}
@@ -376,7 +385,7 @@ void QuickTimeDecoder::init() {
if (_videoStreamIndex >= 0) {
_videoCodec = createCodec(getCodecTag(), getBitsPerPixel());
- if (getScaleMode() != kScaleNormal) {
+ if (getScaleFactorX() != 1 || getScaleFactorY() != 1) {
// We have to initialize the scaled surface
_scaledSurface = new Surface();
_scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel);
@@ -593,17 +602,11 @@ int QuickTimeDecoder::readMVHD(MOVatom atom) {
uint32 yMod = _fd->readUint32BE();
_fd->skip(16);
- if (xMod != yMod)
- error("X and Y resolution modifiers differ");
-
- if (xMod == 0x8000)
- _scaleMode = kScaleHalf;
- else if (xMod == 0x4000)
- _scaleMode = kScaleQuarter;
- else
- _scaleMode = kScaleNormal;
+ _scaleFactorX = Common::Rational(0x10000, xMod);
+ _scaleFactorY = Common::Rational(0x10000, yMod);
- debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode);
+ _scaleFactorX.debugPrint(1, "readMVHD(): scaleFactorX =");
+ _scaleFactorY.debugPrint(1, "readMVHD(): scaleFactorY =");
_fd->readUint32BE(); // preview time
_fd->readUint32BE(); // preview duration
@@ -688,17 +691,11 @@ int QuickTimeDecoder::readTKHD(MOVatom atom) {
uint32 yMod = _fd->readUint32BE();
_fd->skip(16);
- if (xMod != yMod)
- error("X and Y resolution modifiers differ");
+ st->scaleFactorX = Common::Rational(0x10000, xMod);
+ st->scaleFactorY = Common::Rational(0x10000, yMod);
- if (xMod == 0x8000)
- st->scaleMode = kScaleHalf;
- else if (xMod == 0x4000)
- st->scaleMode = kScaleQuarter;
- else
- st->scaleMode = kScaleNormal;
-
- debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode);
+ st->scaleFactorX.debugPrint(1, "readTKHD(): scaleFactorX =");
+ st->scaleFactorY.debugPrint(1, "readTKHD(): scaleFactorY =");
// these are fixed-point, 16:16
// uint32 tkWidth = _fd->readUint32BE() >> 16; // track width
@@ -814,6 +811,17 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) {
_fd->readUint16BE(); // index
debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type);
+
+ if (st->codec_tag && st->codec_tag != format) {
+ // HACK: Multiple FourCC, skip this. FFmpeg does this too and also
+ // skips it with a TODO. However, we really don't need to support
+ // multiple codec tags since the only two videos in Riven DVD that
+ // do this just have a fake second stream (or so it seems).
+ debug(3, "Multiple FourCC not supported");
+ _fd->seek(start_pos + size);
+ continue;
+ }
+
st->codec_tag = format;
if (st->codec_type == CODEC_TYPE_VIDEO) {
diff --git a/graphics/video/qt_decoder.h b/graphics/video/qt_decoder.h
index 196d4c02cb..6dcfc0944d 100644
--- a/graphics/video/qt_decoder.h
+++ b/graphics/video/qt_decoder.h
@@ -36,6 +36,7 @@
#include "common/scummsys.h"
#include "common/queue.h"
+#include "common/rational.h"
#include "graphics/video/video_decoder.h"
#include "graphics/video/codecs/codec.h"
@@ -50,11 +51,6 @@ namespace Common {
namespace Graphics {
-enum ScaleMode {
- kScaleNormal = 1,
- kScaleHalf = 2,
- kScaleQuarter = 4
-};
class QuickTimeDecoder : public RewindableVideoDecoder {
public:
@@ -217,7 +213,8 @@ protected:
uint32 nb_frames;
uint32 duration;
uint32 start_time;
- ScaleMode scaleMode;
+ Common::Rational scaleFactorX;
+ Common::Rational scaleFactorY;
};
const ParseTable *_parseTable;
@@ -230,7 +227,8 @@ protected:
MOVStreamContext *_partial;
uint32 _numStreams;
int _ni;
- ScaleMode _scaleMode;
+ Common::Rational _scaleFactorX;
+ Common::Rational _scaleFactorY;
MOVStreamContext *_streams[20];
byte _palette[256 * 3];
bool _dirtyPalette;
@@ -260,7 +258,8 @@ protected:
Surface *_scaledSurface;
Surface *scaleSurface(Surface *frame);
- ScaleMode getScaleMode() const;
+ Common::Rational getScaleFactorX() const;
+ Common::Rational getScaleFactorY() const;
void pauseVideoIntern(bool pause);
diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp
index 4d03305cce..de1eb4436f 100644
--- a/graphics/video/smk_decoder.cpp
+++ b/graphics/video/smk_decoder.cpp
@@ -252,9 +252,12 @@ BigHuffmanTree::BigHuffmanTree(BitStream &bs, int allocSize)
_loBytes = new SmallHuffmanTree(_bs);
_hiBytes = new SmallHuffmanTree(_bs);
- _markers[0] = _bs.getBits8() | (_bs.getBits8() << 8);
- _markers[1] = _bs.getBits8() | (_bs.getBits8() << 8);
- _markers[2] = _bs.getBits8() | (_bs.getBits8() << 8);
+ _markers[0] = _bs.getBits8();
+ _markers[0] |= (_bs.getBits8() << 8);
+ _markers[1] = _bs.getBits8();
+ _markers[1] |= (_bs.getBits8() << 8);
+ _markers[2] = _bs.getBits8();
+ _markers[2] |= (_bs.getBits8() << 8);
_last[0] = _last[1] = _last[2] = 0xffffffff;
@@ -389,12 +392,13 @@ bool SmackerDecoder::load(Common::SeekableReadStream *stream) {
_frameCount = _fileStream->readUint32LE();
int32 frameRate = _fileStream->readSint32LE();
+ // framerate contains 2 digits after the comma, so 1497 is actually 14.97 fps
if (frameRate > 0)
- _frameRate = 1000 / frameRate;
+ _frameRate = Common::Rational(1000, frameRate);
else if (frameRate < 0)
- _frameRate = 100000 / (-frameRate);
+ _frameRate = Common::Rational(100000, -frameRate);
else
- _frameRate = 10;
+ _frameRate = 1000;
// Flags are determined by which bit is set, which can be one of the following:
// 0 - set to 1 if file contains a ring frame.
@@ -540,53 +544,18 @@ Surface *SmackerDecoder::decodeNextFrame() {
chunkSize = _fileStream->readUint32LE();
chunkSize -= 4; // subtract the first 4 bytes (chunk size)
- if (_header.audioInfo[i].compression != kCompressionNone) {
+ if (_header.audioInfo[i].compression == kCompressionNone) {
+ dataSizeUnpacked = chunkSize;
+ } else {
dataSizeUnpacked = _fileStream->readUint32LE();
chunkSize -= 4; // subtract the next 4 bytes (unpacked data size)
- } else {
- dataSizeUnpacked = 0;
}
- if (_header.audioInfo[i].hasAudio && chunkSize > 0 && i == 0) {
- // If it's track 0, play the audio data
- byte *soundBuffer = (byte *)malloc(chunkSize);
-
- _fileStream->read(soundBuffer, chunkSize);
-
- if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT) {
- // TODO: Compressed audio (Bink RDFT/DCT encoded)
- free(soundBuffer);
- continue;
- } else if (_header.audioInfo[i].compression == kCompressionDPCM) {
- // Compressed audio (Huffman DPCM encoded)
- queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i);
- free(soundBuffer);
- } else {
- // Uncompressed audio (PCM)
- byte flags = 0;
- if (_header.audioInfo[0].is16Bits)
- flags = flags | Audio::FLAG_16BITS;
- if (_header.audioInfo[0].isStereo)
- flags = flags | Audio::FLAG_STEREO;
-
- _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags);
- // The sound buffer will be deleted by QueuingAudioStream
- }
-
- if (!_audioStarted) {
- _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255);
- _audioStarted = true;
- }
- } else {
- // Ignore the rest of the audio tracks, if they exist
- // TODO: Are there any Smacker videos with more than one audio stream?
- // If yes, we should play the rest of the audio streams as well
- if (chunkSize > 0)
- _fileStream->skip(chunkSize);
- }
+ handleAudioTrack(i, chunkSize, dataSizeUnpacked);
}
uint32 frameSize = _frameSizes[_curFrame] & ~3;
+// uint32 remainder = _frameSizes[_curFrame] & 3;
if (_fileStream->pos() - startPos > frameSize)
error("Smacker actual frame size exceeds recorded frame size");
@@ -746,6 +715,46 @@ Surface *SmackerDecoder::decodeNextFrame() {
return _surface;
}
+void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
+ if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) {
+ // If it's track 0, play the audio data
+ byte *soundBuffer = (byte *)malloc(chunkSize);
+
+ _fileStream->read(soundBuffer, chunkSize);
+
+ if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) {
+ // TODO: Compressed audio (Bink RDFT/DCT encoded)
+ free(soundBuffer);
+ return;
+ } else if (_header.audioInfo[track].compression == kCompressionDPCM) {
+ // Compressed audio (Huffman DPCM encoded)
+ queueCompressedBuffer(soundBuffer, chunkSize, unpackedSize, track);
+ free(soundBuffer);
+ } else {
+ // Uncompressed audio (PCM)
+ byte flags = 0;
+ if (_header.audioInfo[track].is16Bits)
+ flags = flags | Audio::FLAG_16BITS;
+ if (_header.audioInfo[track].isStereo)
+ flags = flags | Audio::FLAG_STEREO;
+
+ _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags);
+ // The sound buffer will be deleted by QueuingAudioStream
+ }
+
+ if (!_audioStarted) {
+ _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255);
+ _audioStarted = true;
+ }
+ } else {
+ // Ignore the rest of the audio tracks, if they exist
+ // TODO: Are there any Smacker videos with more than one audio stream?
+ // If yes, we should play the rest of the audio streams as well
+ if (chunkSize > 0)
+ _fileStream->skip(chunkSize);
+ }
+}
+
void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
uint32 unpackedSize, int streamNum) {
@@ -774,13 +783,23 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
int32 bases[2];
- if (isStereo)
- bases[1] = (!is16Bits) ? audioBS.getBits8() :
- ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8())));
-
- bases[0] = (!is16Bits) ? audioBS.getBits8() :
- ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8())));
+ if (isStereo) {
+ if (is16Bits) {
+ byte hi = audioBS.getBits8();
+ byte lo = audioBS.getBits8();
+ bases[1] = (int16) ((hi << 8) | lo);
+ } else {
+ bases[1] = audioBS.getBits8();
+ }
+ }
+ if (is16Bits) {
+ byte hi = audioBS.getBits8();
+ byte lo = audioBS.getBits8();
+ bases[0] = (int16) ((hi << 8) | lo);
+ } else {
+ bases[0] = audioBS.getBits8();
+ }
// The bases are the first samples, too
for (int i = 0; i < (isStereo ? 2 : 1); i++, curPointer += (is16Bits ? 2 : 1), curPos += (is16Bits ? 2 : 1)) {
@@ -805,10 +824,11 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
}
} else {
for (int k = 0; k < (isStereo ? 2 : 1); k++) {
- bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) |
- (audioTrees[k * 2 + 1]->getCode(audioBS) << 8));
+ byte lo = audioTrees[k * 2]->getCode(audioBS);
+ byte hi = audioTrees[k * 2 + 1]->getCode(audioBS);
+ bases[k] += (int16) (lo | (hi << 8));
- WRITE_BE_UINT16(curPointer, CLIP<int32>(bases[k], -32768, 32767));
+ WRITE_BE_UINT16(curPointer, bases[k]);
curPointer += 2;
curPos += 2;
}
diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h
index 43bb84a4f8..5faeab4343 100644
--- a/graphics/video/smk_decoder.h
+++ b/graphics/video/smk_decoder.h
@@ -69,12 +69,13 @@ public:
PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
byte *getPalette() { _dirtyPalette = false; return _palette; }
bool hasDirtyPalette() const { return _dirtyPalette; }
+ virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
protected:
Common::Rational getFrameRate() const { return _frameRate; }
Common::SeekableReadStream *_fileStream;
-private:
+protected:
void unpackPalette();
// Possible runs of blocks
uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); }
@@ -120,7 +121,7 @@ private:
byte *_palette;
bool _dirtyPalette;
- uint32 _frameRate;
+ Common::Rational _frameRate;
uint32 _frameCount;
Surface *_surface;
diff --git a/gui/EditTextWidget.cpp b/gui/EditTextWidget.cpp
index 7079453173..e5de1c6e7f 100644
--- a/gui/EditTextWidget.cpp
+++ b/gui/EditTextWidget.cpp
@@ -88,7 +88,7 @@ void EditTextWidget::drawWidget() {
}
Common::Rect EditTextWidget::getEditRect() const {
- Common::Rect r(2 + _leftPadding, 1, _w - 2 - _leftPadding - _rightPadding, _h-1);
+ Common::Rect r(2 + _leftPadding, 2, _w - 2 - _leftPadding - _rightPadding, _h-1);
return r;
}
diff --git a/gui/GuiManager.cpp b/gui/GuiManager.cpp
index bbd7718d71..a2a8654bf6 100644
--- a/gui/GuiManager.cpp
+++ b/gui/GuiManager.cpp
@@ -63,6 +63,9 @@ GuiManager::GuiManager() : _redrawStatus(kRedrawDisabled), _tooltipCheck(false),
// Clear the cursor
memset(_cursor, 0xFF, sizeof(_cursor));
+ // Enable translation
+ TransMan.setLanguage(ConfMan.get("gui_language").c_str());
+
ConfMan.registerDefault("gui_theme", "scummmodern");
Common::String themefile(ConfMan.get("gui_theme"));
diff --git a/gui/KeysDialog.h b/gui/KeysDialog.h
index d18bcb3617..7a9936a085 100644
--- a/gui/KeysDialog.h
+++ b/gui/KeysDialog.h
@@ -30,12 +30,13 @@
#include "gui/dialog.h"
#include "gui/ListWidget.h"
#include "common/str.h"
+#include "common/translation.h"
namespace GUI {
class KeysDialog : public GUI::Dialog {
public:
- KeysDialog(const Common::String &title = "Choose an action to map");
+ KeysDialog(const Common::String &title = _("Choose an action to map"));
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
virtual void handleKeyUp(Common::KeyState state);
diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp
index 4447b62244..f5f41bba80 100644
--- a/gui/ListWidget.cpp
+++ b/gui/ListWidget.cpp
@@ -449,11 +449,15 @@ bool ListWidget::handleKeyUp(Common::KeyState state) {
}
void ListWidget::receivedFocusWidget() {
+ _inversion = ThemeEngine::kTextInversionFocus;
+
// Redraw the widget so the selection color will change
draw();
}
void ListWidget::lostFocusWidget() {
+ _inversion = ThemeEngine::kTextInversion;
+
// If we lose focus, we simply forget the user changes
_editMode = false;
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
@@ -491,12 +495,8 @@ void ListWidget::drawWidget() {
ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
// Draw the selected item inverted, on a highlighted background.
- if (_selectedItem == pos) {
- if (_hasFocus)
- inverted = ThemeEngine::kTextInversionFocus;
- else
- inverted = ThemeEngine::kTextInversion;
- }
+ if (_selectedItem == pos)
+ inverted = _inversion;
Common::Rect r(getEditRect());
int pad = _leftPadding;
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 9cc98d83ce..d9a1e05855 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -44,8 +44,6 @@
#include "gui/ThemeEval.h"
#include "gui/ThemeParser.h"
-#define GUI_ENABLE_BUILTIN_THEME
-
namespace GUI {
const char * const ThemeEngine::kImageLogo = "logo.bmp";
@@ -331,15 +329,12 @@ ThemeEngine::~ThemeEngine() {
* Rendering mode management
*********************************************************/
const ThemeEngine::Renderer ThemeEngine::_rendererModes[] = {
- { _s("Disabled GFX"), "none", kGfxDisabled },
- { _s("Standard Renderer (16bpp)"), "normal_16bpp", kGfxStandard16bit },
+ { _s("Disabled GFX"), _sc("Disabled GFX", "lowres"), "none", kGfxDisabled },
+ { _s("Standard Renderer (16bpp)"), _s("Standard (16bpp)"), "normal_16bpp", kGfxStandard16bit },
#ifndef DISABLE_FANCY_THEMES
- { _s("Antialiased Renderer (16bpp)"), "aa_16bpp", kGfxAntialias16bit }
+ { _s("Antialiased Renderer (16bpp)"), _s("Antialiased (16bpp)"), "aa_16bpp", kGfxAntialias16bit }
#endif
};
-
-DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Standard Renderer (16bpp)", "lowres")
-DECLARE_TRANSLATION_ADDITIONAL_CONTEXT("Antialiased Renderer (16bpp)", "lowres")
const uint ThemeEngine::_rendererModesSize = ARRAYSIZE(ThemeEngine::_rendererModes);
@@ -578,25 +573,26 @@ bool ThemeEngine::addFont(TextData textId, const Common::String &file) {
if (!_texts[textId]->_fontPtr) {
// First try to load localized font
_texts[textId]->_fontPtr = loadFont(localized);
-
+
if (_texts[textId]->_fontPtr)
FontMan.assignFontToName(file, _texts[textId]->_fontPtr);
- // Fallback to non-localized font
+ // Fallback to non-localized font and default translation
else {
// Try built-in fonts
_texts[textId]->_fontPtr = FontMan.getFontByName(file);
-
+
// Try to load it
if (!_texts[textId]->_fontPtr) {
_texts[textId]->_fontPtr = loadFont(file);
if (!_texts[textId]->_fontPtr)
error("Couldn't load font '%s'", file.c_str());
-
+
FontMan.assignFontToName(file, _texts[textId]->_fontPtr);
}
- warning("Failed to load localized font '%s'. Using non-localized font instead", file.c_str());
+ TransMan.setLanguage("C");
+ warning("Failed to load localized font '%s'. Using non-localized font and default GUI language instead", file.c_str());
}
}
}
@@ -716,7 +712,7 @@ bool ThemeEngine::loadDefaultXML() {
// file inside the themes directory.
// Use the Python script "makedeftheme.py" to convert a normal XML theme
// into the "default.inc" file, which is ready to be included in the code.
-#ifdef GUI_ENABLE_BUILTIN_THEME
+#ifndef DISABLE_GUI_BUILTIN_THEME
const char *defaultXML =
#include "themes/default.inc"
;
@@ -1192,70 +1188,6 @@ void ThemeEngine::debugWidgetPosition(const char *name, const Common::Rect &r) {
_screen.vLine(r.right, r.top, r.bottom, 0xFFFF);
}
-ThemeEngine::StoredState *ThemeEngine::storeState(const Common::Rect &r) {
- StoredState *state = new StoredState;
- byte *dst;
- byte *src;
-
- state->r.top = r.top;
- state->r.bottom = r.bottom;
- state->r.left = r.left;
- state->r.right = r.right;
-
- state->r.clip(_screen.w, _screen.h);
-
- state->screen.create(state->r.width(), state->r.height(), _screen.bytesPerPixel);
- state->backBuffer.create(state->r.width(), state->r.height(), _backBuffer.bytesPerPixel);
-
- src = (byte *)_screen.getBasePtr(state->r.left, state->r.top);
- dst = (byte *)state->screen.getBasePtr(0, 0);
-
- for (int i = state->r.height(); i > 0; i--) {
- memcpy(dst, src, state->r.width() * _screen.bytesPerPixel);
- src += _screen.pitch;
- dst += state->screen.pitch;
- }
-
- src = (byte *)_backBuffer.getBasePtr(state->r.left, state->r.top);
- dst = (byte *)state->backBuffer.getBasePtr(0, 0);
-
- for (int i = state->r.height(); i > 0; i--) {
- memcpy(dst, src, state->r.width() * _backBuffer.bytesPerPixel);
- src += _backBuffer.pitch;
- dst += state->backBuffer.pitch;
- }
-
- return state;
-}
-
-void ThemeEngine::restoreState(StoredState *state) {
- byte *dst;
- byte *src;
-
- if (!state)
- return;
-
- src = (byte *)state->screen.getBasePtr(0, 0);
- dst = (byte *)_screen.getBasePtr(state->r.left, state->r.top);
-
- for (int i = state->r.height(); i > 0; i--) {
- memcpy(dst, src, state->r.width() * _screen.bytesPerPixel);
- src += state->screen.pitch;
- dst += _screen.pitch;
- }
-
- src = (byte *)state->backBuffer.getBasePtr(0, 0);
- dst = (byte *)_backBuffer.getBasePtr(state->r.left, state->r.top);
-
- for (int i = state->r.height(); i > 0; i--) {
- memcpy(dst, src, state->r.width() * _backBuffer.bytesPerPixel);
- src += state->backBuffer.pitch;
- dst += _backBuffer.pitch;
- }
-
- addDirtyRect(state->r);
-}
-
/**********************************************************
* Screen/overlay management
*********************************************************/
@@ -1661,7 +1593,7 @@ struct TDComparator {
} // end of anonymous namespace
void ThemeEngine::listUsableThemes(Common::List<ThemeDescriptor> &list) {
-#ifdef GUI_ENABLE_BUILTIN_THEME
+#ifndef DISABLE_GUI_BUILTIN_THEME
ThemeDescriptor th;
th.name = "ScummVM Classic Theme (Builtin Version)";
th.id = "builtin";
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 73181b74a6..3d2fca04eb 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -238,6 +238,7 @@ public:
struct Renderer {
const char *name;
+ const char *shortname;
const char *cfg;
GraphicsMode mode;
};
@@ -263,17 +264,6 @@ public:
void enable();
void disable();
- struct StoredState {
- Common::Rect r;
- Graphics::Surface screen;
- Graphics::Surface backBuffer;
-
- StoredState() {}
- };
-
- StoredState *storeState(const Common::Rect &r);
- void restoreState(StoredState *state);
-
/**
* Implementation of the GUI::Theme API. Called when a
* new dialog is opened. Note that the boolean parameter
diff --git a/gui/ThemeEval.h b/gui/ThemeEval.h
index c484a49564..ac867512da 100644
--- a/gui/ThemeEval.h
+++ b/gui/ThemeEval.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef GUI_THEME_EVAL
-#define GUI_THEME_EVAL
+#ifndef GUI_THEME_EVAL_H
+#define GUI_THEME_EVAL_H
#include "common/scummsys.h"
#include "common/hashmap.h"
diff --git a/gui/ThemeParser.cpp b/gui/ThemeParser.cpp
index c809447cbd..0daf2528dd 100644
--- a/gui/ThemeParser.cpp
+++ b/gui/ThemeParser.cpp
@@ -144,7 +144,7 @@ Graphics::DrawStep *ThemeParser::defaultDrawStep() {
Graphics::DrawStep *ThemeParser::newDrawStep() {
assert(_defaultStepGlobal);
- Graphics::DrawStep *step = 0 ; //new DrawStep;
+ Graphics::DrawStep *step = 0; //new DrawStep;
if (_defaultStepLocal) {
step = new Graphics::DrawStep(*_defaultStepLocal);
@@ -195,7 +195,7 @@ bool ThemeParser::parserCallback_text_color(ParserNode *node) {
if (_palette.contains(node->values["color"]))
getPaletteColor(node->values["color"], red, green, blue);
- else if (!parseIntegerKey(node->values["color"].c_str(), 3, &red, &green, &blue))
+ else if (!parseIntegerKey(node->values["color"], 3, &red, &green, &blue))
return parserError("Error parsing color value for text color definition.");
if (!_theme->addTextColor(colorId, red, green, blue))
@@ -216,10 +216,10 @@ bool ThemeParser::parserCallback_cursor(ParserNode *node) {
int spotx, spoty, scale;
- if (!parseIntegerKey(node->values["hotspot"].c_str(), 2, &spotx, &spoty))
+ if (!parseIntegerKey(node->values["hotspot"], 2, &spotx, &spoty))
return parserError("Error parsing cursor Hot Spot coordinates.");
- if (!parseIntegerKey(node->values["scale"].c_str(), 1, &scale))
+ if (!parseIntegerKey(node->values["scale"], 1, &scale))
return parserError("Error parsing cursor scale.");
if (!_theme->createCursor(node->values["file"], spotx, spoty, scale))
@@ -286,7 +286,7 @@ bool ThemeParser::parserCallback_color(ParserNode *node) {
int red, green, blue;
- if (parseIntegerKey(node->values["rgb"].c_str(), 3, &red, &green, &blue) == false ||
+ if (parseIntegerKey(node->values["rgb"], 3, &red, &green, &blue) == false ||
red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255)
return parserError("Error parsing RGB values for palette color '%s'", name.c_str());\
@@ -387,7 +387,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
*/
#define __PARSER_ASSIGN_INT(struct_name, key_name, force) \
if (stepNode->values.contains(key_name)) { \
- if (!parseIntegerKey(stepNode->values[key_name].c_str(), 1, &x)) \
+ if (!parseIntegerKey(stepNode->values[key_name], 1, &x)) \
return parserError("Error parsing key value for '%s'.", key_name); \
\
drawstep->struct_name = x; \
@@ -411,7 +411,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
red = _palette[val].r; \
green = _palette[val].g; \
blue = _palette[val].b; \
- } else if (parseIntegerKey(val.c_str(), 3, &red, &green, &blue) == false || \
+ } else if (parseIntegerKey(val, 3, &red, &green, &blue) == false || \
red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) \
return parserError("Error parsing color struct '%s'", val.c_str());\
\
@@ -481,7 +481,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
drawstep->autoWidth = false;
val = stepNode->values["width"];
- if (parseIntegerKey(val.c_str(), 1, &x))
+ if (parseIntegerKey(val, 1, &x))
drawstep->w = x;
else if (val == "height")
drawstep->w = -1;
@@ -490,7 +490,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
if (stepNode->values.contains("xpos")) {
val = stepNode->values["xpos"];
- if (parseIntegerKey(val.c_str(), 1, &x))
+ if (parseIntegerKey(val, 1, &x))
drawstep->x = x;
else if (val == "center")
drawstep->xAlign = Graphics::DrawStep::kVectorAlignCenter;
@@ -509,7 +509,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
drawstep->autoHeight = false;
val = stepNode->values["height"];
- if (parseIntegerKey(val.c_str(), 1, &x))
+ if (parseIntegerKey(val, 1, &x))
drawstep->h = x;
else if (val == "width")
drawstep->h = -1;
@@ -518,7 +518,7 @@ bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawst
if (stepNode->values.contains("ypos")) {
val = stepNode->values["ypos"];
- if (parseIntegerKey(val.c_str(), 1, &x))
+ if (parseIntegerKey(val, 1, &x))
drawstep->y = x;
else if (val == "center")
drawstep->yAlign = Graphics::DrawStep::kVectorAlignCenter;
@@ -569,7 +569,7 @@ bool ThemeParser::parserCallback_def(ParserNode *node) {
if (_theme->getEvaluator()->hasVar(node->values["value"]) == true)
value = _theme->getEvaluator()->getVar(node->values["value"]);
- else if (!parseIntegerKey(node->values["value"].c_str(), 1, &value))
+ else if (!parseIntegerKey(node->values["value"], 1, &value))
return parserError("Invalid definition for '%s'.", var.c_str());
_theme->getEvaluator()->setVar(var, value);
@@ -608,7 +608,7 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) {
if (_theme->getEvaluator()->hasVar(node->values["width"]) == true)
width = _theme->getEvaluator()->getVar(node->values["width"]);
- else if (!parseIntegerKey(node->values["width"].c_str(), 1, &width))
+ else if (!parseIntegerKey(node->values["width"], 1, &width))
return parserError("Corrupted width value in key for %s", var.c_str());
}
@@ -616,7 +616,7 @@ bool ThemeParser::parserCallback_widget(ParserNode *node) {
if (_theme->getEvaluator()->hasVar(node->values["height"]) == true)
height = _theme->getEvaluator()->getVar(node->values["height"]);
- else if (!parseIntegerKey(node->values["height"].c_str(), 1, &height))
+ else if (!parseIntegerKey(node->values["height"], 1, &height))
return parserError("Corrupted height value in key for %s", var.c_str());
}
@@ -651,7 +651,7 @@ bool ThemeParser::parserCallback_dialog(ParserNode *node) {
}
if (node->values.contains("inset")) {
- if (!parseIntegerKey(node->values["inset"].c_str(), 1, &inset))
+ if (!parseIntegerKey(node->values["inset"], 1, &inset))
return false;
}
@@ -682,7 +682,7 @@ bool ThemeParser::parserCallback_layout(ParserNode *node) {
int spacing = -1;
if (node->values.contains("spacing")) {
- if (!parseIntegerKey(node->values["spacing"].c_str(), 1, &spacing))
+ if (!parseIntegerKey(node->values["spacing"], 1, &spacing))
return false;
}
@@ -697,7 +697,7 @@ bool ThemeParser::parserCallback_layout(ParserNode *node) {
if (node->values.contains("padding")) {
int paddingL, paddingR, paddingT, paddingB;
- if (!parseIntegerKey(node->values["padding"].c_str(), 4, &paddingL, &paddingR, &paddingT, &paddingB))
+ if (!parseIntegerKey(node->values["padding"], 4, &paddingL, &paddingR, &paddingT, &paddingB))
return false;
_theme->getEvaluator()->addPadding(paddingL, paddingR, paddingT, paddingB);
@@ -713,7 +713,7 @@ bool ThemeParser::parserCallback_space(ParserNode *node) {
if (_theme->getEvaluator()->hasVar(node->values["size"]))
size = _theme->getEvaluator()->getVar(node->values["size"]);
- else if (!parseIntegerKey(node->values["size"].c_str(), 1, &size))
+ else if (!parseIntegerKey(node->values["size"], 1, &size))
return parserError("Invalid value for Spacing size.");
}
@@ -734,7 +734,7 @@ bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String
if (node->values.contains("size")) {
int width, height;
- if (!parseIntegerKey(node->values["size"].c_str(), 2, &width, &height)) {
+ if (!parseIntegerKey(node->values["size"], 2, &width, &height)) {
Common::StringTokenizer tokenizer(node->values["size"], " ,");
Common::String wtoken, htoken;
char *parseEnd;
@@ -779,7 +779,7 @@ bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String
if (node->values.contains("pos")) {
int x, y;
- if (!parseIntegerKey(node->values["pos"].c_str(), 2, &x, &y)) {
+ if (!parseIntegerKey(node->values["pos"], 2, &x, &y)) {
Common::StringTokenizer tokenizer(node->values["pos"], " ,");
Common::String xpos, ypos;
char *parseEnd;
@@ -835,7 +835,7 @@ bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String
if (node->values.contains("padding")) {
int paddingL, paddingR, paddingT, paddingB;
- if (!parseIntegerKey(node->values["padding"].c_str(), 4, &paddingL, &paddingR, &paddingT, &paddingB))
+ if (!parseIntegerKey(node->values["padding"], 4, &paddingL, &paddingR, &paddingT, &paddingB))
return false;
_theme->getEvaluator()->setVar(var + "Padding.Left", paddingL);
diff --git a/gui/Tooltip.cpp b/gui/Tooltip.cpp
index 64c34688df..272fa66072 100644
--- a/gui/Tooltip.cpp
+++ b/gui/Tooltip.cpp
@@ -34,7 +34,7 @@
namespace GUI {
-Tooltip::Tooltip() :
+Tooltip::Tooltip() :
Dialog(-1, -1, -1, -1), _maxWidth(-1) {
_backgroundType = GUI::ThemeEngine::kDialogBackgroundTooltip;
@@ -69,7 +69,7 @@ bool Tooltip::tooltipModal(int x, int y) {
_h = (tooltipFont->getFontHeight() + 2) * _wrappedLines.size();
_x = MIN<int16>(g_gui.getTopDialog()->_x + x + _xdelta, g_gui.getWidth() - _w - 3);
- _y = MIN<int16>(g_gui.getTopDialog()->_y + y + _ydelta, g_gui.getHeight() - _h - 3);
+ _y = MIN<int16>(g_gui.getTopDialog()->_y + y + _ydelta, g_gui.getHeight() - _h - 3);
open();
g_gui.runLoop();
@@ -85,14 +85,14 @@ void Tooltip::drawDialog() {
for (Common::StringArray::const_iterator i = _wrappedLines.begin(); i != _wrappedLines.end(); ++i, ++num) {
g_gui.theme()->drawText(
- Common::Rect(_x + 1, _y + 1 + num * h, _x + 1 +_w, _y + 1+ (num + 1) * h), *i,
- ThemeEngine::kStateEnabled,
- Graphics::kTextAlignLeft,
- ThemeEngine::kTextInversionNone,
+ Common::Rect(_x + 1, _y + 1 + num * h, _x + 1 +_w, _y + 1+ (num + 1) * h), *i,
+ ThemeEngine::kStateEnabled,
+ Graphics::kTextAlignLeft,
+ ThemeEngine::kTextInversionNone,
0,
- false,
- ThemeEngine::kFontStyleTooltip,
- ThemeEngine::kFontColorNormal,
+ false,
+ ThemeEngine::kFontStyleTooltip,
+ ThemeEngine::kFontColorNormal,
false
);
}
diff --git a/gui/Tooltip.h b/gui/Tooltip.h
index 60f3cf3a19..02eb01d0d0 100644
--- a/gui/Tooltip.h
+++ b/gui/Tooltip.h
@@ -33,7 +33,7 @@ class Tooltip : public Dialog {
public:
Tooltip();
~Tooltip() {}
-
+
void drawDialog();
bool tooltipModal(int x, int y);
void mustClose();
@@ -44,7 +44,8 @@ protected:
int _xdelta, _ydelta;
Common::StringArray _wrappedLines;
-};
-}
+};
+
+} // End of namespace GUI
-#endif
+#endif // GUI_TOOLTIP_H
diff --git a/gui/browser.cpp b/gui/browser.cpp
index 0d95e5397b..6a9d3d05b5 100644
--- a/gui/browser.cpp
+++ b/gui/browser.cpp
@@ -66,7 +66,10 @@ BrowserDialog::BrowserDialog(const char *title, bool dirBrowser)
_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;
// Buttons
- new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(this, "Browser.Up", _("Go up"), _("Go to previous directory level"), kGoUpCmd);
+ else
+ new ButtonWidget(this, "Browser.Up", _c("Go up", "lowres"), _("Go to previous directory level"), kGoUpCmd);
new ButtonWidget(this, "Browser.Cancel", _("Cancel"), 0, kCloseCmd);
new ButtonWidget(this, "Browser.Choose", _("Choose"), 0, kChooseCmd);
}
diff --git a/gui/browser.h b/gui/browser.h
index 8dc7eda43a..1fef041a5a 100644
--- a/gui/browser.h
+++ b/gui/browser.h
@@ -29,10 +29,6 @@
#include "common/str.h"
#include "common/fs.h"
-#ifdef MACOSX
-#include <Carbon/Carbon.h>
-#endif
-
namespace GUI {
class ListWidget;
@@ -54,7 +50,7 @@ public:
protected:
#ifdef MACOSX
- CFStringRef _titleRef;
+ const void *_titleRef;
#else
ListWidget *_fileList;
StaticTextWidget *_currentPath;
diff --git a/gui/browser_osx.mm b/gui/browser_osx.mm
index a3a09b8ed2..ea77e16c04 100644
--- a/gui/browser_osx.mm
+++ b/gui/browser_osx.mm
@@ -22,6 +22,9 @@
* $Id$
*/
+// Disable symbol overrides so that we can use system headers
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "gui/browser.h"
#include "gui/GuiManager.h"
#include "gui/ListWidget.h"
diff --git a/gui/credits.h b/gui/credits.h
index bd4ecffc9b..a2c3bd681a 100644
--- a/gui/credits.h
+++ b/gui/credits.h
@@ -119,6 +119,11 @@ static const char *credits[] = {
"C0""Scott Thomas",
"C0""Jordi Vilalta Prat",
"",
+"C1""Hugo",
+"C0""Arnaud Boutonn\351",
+"C0""Oystein Eftevaag",
+"C0""Eugene Sandulenko",
+"",
"C1""Kyra",
"C0""Torbj\366rn Andersson",
"C2""VQA Player",
@@ -127,6 +132,11 @@ static const char *credits[] = {
"C0""Gregory Montoir",
"C0""Johannes Schickel",
"",
+"C1""Last Express",
+"C0""Matthew Hoops",
+"C0""Jordi Vilalta Prat",
+"C0""Julien Templier",
+"",
"C1""Lure",
"C0""Paul Gilbert",
"",
@@ -181,6 +191,9 @@ static const char *credits[] = {
"C0""Filippos Karapetis",
"C0""Joost Peters",
"",
+"C1""Toon",
+"C0""Sylvain Dupont",
+"",
"C1""Touch\351",
"C0""Gregory Montoir",
"",
@@ -195,7 +208,7 @@ static const char *credits[] = {
"C1""Dreamcast",
"C0""Marcus Comstedt",
"",
-"C1""GP2X",
+"C1""GPH Devices (GP2X, GP2XWiz & Caanoo)",
"C0""John Willis",
"",
"C1""iPhone",
@@ -213,6 +226,9 @@ static const char *credits[] = {
"C1""Nintendo DS",
"C0""Neil Millstone",
"",
+"C1""OpenPandora",
+"C0""John Willis",
+"",
"C1""PocketPC / WinCE",
"C0""Nicolas Bacca",
"C2""(retired)",
@@ -544,6 +560,8 @@ static const char *credits[] = {
"C2""For the original Gobliiins ADL player",
"C0""Ivan Dubrov",
"C2""For contributing the initial version of the Gobliiins engine",
+"C0""Henrik Engqvist",
+"C2""For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space",
"C0""DOSBox Team",
"C2""For their awesome OPL2 and OPL3 emulator",
"C0""Till Kresslein",
@@ -574,5 +592,11 @@ static const char *credits[] = {
"C0""",
"C0""John Young, Colin Smythe and especially Terry Pratchett himself for sharing the source code of Discworld I & II with us.",
"C0""",
+"C0""Emilio de Paz Aragon from Alcachofa Soft for sharing the source code of Drascula: The Vampire Strikes Back with us and his generosity with freewaring the game.",
+"C0""",
+"C0""David P. Gray from Gray Design Associate for sharing the source code of the Hugo trilogy.",
+"C0""",
+"C0""Broken Sword 2.5 team for providing sources of their engine and their great support.",
+"C0""",
"",
};
diff --git a/gui/editable.cpp b/gui/editable.cpp
index 755e34e380..1ebe307bb0 100644
--- a/gui/editable.cpp
+++ b/gui/editable.cpp
@@ -48,6 +48,7 @@ void EditableWidget::init() {
_editScrollOffset = 0;
_font = ThemeEngine::kFontStyleBold;
+ _inversion = ThemeEngine::kTextInversionNone;
}
EditableWidget::~EditableWidget() {
@@ -237,7 +238,7 @@ void EditableWidget::drawCaret(bool erase) {
Common::Rect editRect = getEditRect();
int x = editRect.left;
- int y = editRect.top + 1;
+ int y = editRect.top;
x += getCaretOffset();
@@ -253,7 +254,7 @@ void EditableWidget::drawCaret(bool erase) {
if ((uint)_caretPos < _editString.size()) {
GUI::EditableWidget::String chr(_editString[_caretPos]);
int chrWidth = g_gui.getCharWidth(_editString[_caretPos], _font);
- g_gui.theme()->drawText(Common::Rect(x, y, x + chrWidth, y + editRect.height() - 2), chr, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, 0, false, _font);
+ g_gui.theme()->drawText(Common::Rect(x, y, x + chrWidth, y + editRect.height() - 2), chr, _state, Graphics::kTextAlignLeft, _inversion, 0, false, _font);
}
}
diff --git a/gui/editable.h b/gui/editable.h
index 8ff5298511..4a2d98349e 100644
--- a/gui/editable.h
+++ b/gui/editable.h
@@ -54,6 +54,8 @@ protected:
ThemeEngine::FontStyle _font;
+ ThemeEngine::TextInversionState _inversion;
+
public:
EditableWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip = 0, uint32 cmd = 0);
EditableWidget(GuiObject *boss, const String &name, const char *tooltip = 0, uint32 cmd = 0);
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 0a3aa80f58..b3d08267f0 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -172,11 +172,17 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
tab->addTab(_("Game"));
// GUI: Label & edit widget for the game ID
- new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to savegames and running the game from the command line"));
+ if (g_system->getOverlayWidth() > 320)
+ new StaticTextWidget(tab, "GameOptions_Game.Id", _("ID:"), _("Short game identifier used for referring to savegames and running the game from the command line"));
+ else
+ new StaticTextWidget(tab, "GameOptions_Game.Id", _c("ID:", "lowres"), _("Short game identifier used for referring to savegames and running the game from the command line"));
_domainWidget = new DomainEditTextWidget(tab, "GameOptions_Game.Domain", _domain, _("Short game identifier used for referring to savegames and running the game from the command line"));
// GUI: Label & edit widget for the description
- new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game"));
+ if (g_system->getOverlayWidth() > 320)
+ new StaticTextWidget(tab, "GameOptions_Game.Name", _("Name:"), _("Full title of the game"));
+ else
+ new StaticTextWidget(tab, "GameOptions_Game.Name", _c("Name:", "lowres"), _("Full title of the game"));
_descriptionWidget = new EditTextWidget(tab, "GameOptions_Game.Desc", description, _("Full title of the game"));
// Language popup
@@ -276,17 +282,26 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
//
// 7) The Paths tab
//
- tab->addTab(_("Paths"));
+ if (g_system->getOverlayWidth() > 320)
+ tab->addTab(_("Paths"));
+ else
+ tab->addTab(_c("Paths", "lowres"));
// These buttons have to be extra wide, or the text will be truncated
// in the small version of the GUI.
// GUI: Button + Label for the game path
- new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), 0, kCmdGameBrowser);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _("Game Path:"), 0, kCmdGameBrowser);
+ else
+ new ButtonWidget(tab, "GameOptions_Paths.Gamepath", _c("Game Path:", "lowres"), 0, kCmdGameBrowser);
_gamePathWidget = new StaticTextWidget(tab, "GameOptions_Paths.GamepathText", gamePath);
// GUI: Button + Label for the additional path
- new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used the game"), kCmdExtraBrowser);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _("Extra Path:"), _("Specifies path to additional data used the game"), kCmdExtraBrowser);
+ else
+ new ButtonWidget(tab, "GameOptions_Paths.Extrapath", _c("Extra Path:", "lowres"), _("Specifies path to additional data used the game"), kCmdExtraBrowser);
_extraPathWidget = new StaticTextWidget(tab, "GameOptions_Paths.ExtrapathText", extraPath, _("Specifies path to additional data used the game"));
// GUI: Button + Label for the save path
@@ -1044,7 +1059,7 @@ void LauncherDialog::updateButtons() {
int modifiers = g_system->getEventManager()->getModifierState();
const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0;
const bool lowRes = g_system->getOverlayWidth() <= 320;
-
+
const char *newAddButtonLabel = massAdd
? (lowRes ? _c("Mass Add...", "lowres") : _("Mass Add..."))
: (lowRes ? _c("Add Game...", "lowres") : _("Add Game..."));
diff --git a/gui/message.cpp b/gui/message.cpp
index 6406976569..06b4524eab 100644
--- a/gui/message.cpp
+++ b/gui/message.cpp
@@ -58,7 +58,11 @@ MessageDialog::MessageDialog(const Common::String &message, const char *defaultB
int maxlineWidth = g_gui.getFont().wordWrapText(message, screenW - 2 * 20, lines);
// Calculate the desired dialog size (maxing out at 300*180 for now)
- _w = maxlineWidth + 20;
+ if (altButton)
+ _w = MAX(maxlineWidth, (2 * buttonWidth) + 10) + 20;
+ else
+ _w = MAX(maxlineWidth, buttonWidth) + 20;
+
lineCount = lines.size();
_h = 16;
diff --git a/gui/options.cpp b/gui/options.cpp
index 7c7093fa95..558a89386b 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -213,14 +213,8 @@ void OptionsDialog::open() {
}
if (_multiMidiCheckbox) {
- if (!loadMusicDeviceSetting(_gmDevicePopUp, "gm_device")) {
- if (_domain.equals(Common::ConfigManager::kApplicationDomain)) {
- if (!loadMusicDeviceSetting(_gmDevicePopUp, Common::String(), MT_GM))
- _gmDevicePopUp->setSelected(0);
- } else {
- _gmDevicePopUp->setSelected(0);
- }
- }
+ if (!loadMusicDeviceSetting(_gmDevicePopUp, "gm_device"))
+ _gmDevicePopUp->setSelected(0);
// Multi midi setting
_multiMidiCheckbox->setState(ConfMan.getBool("multi_midi", _domain));
@@ -244,14 +238,8 @@ void OptionsDialog::open() {
// MT-32 options
if (_mt32DevicePopUp) {
- if (!loadMusicDeviceSetting(_mt32DevicePopUp, "mt32_device")) {
- if (_domain.equals(Common::ConfigManager::kApplicationDomain)) {
- if (!loadMusicDeviceSetting(_mt32DevicePopUp, Common::String(), MT_MT32))
- _mt32DevicePopUp->setSelected(0);
- } else {
- _mt32DevicePopUp->setSelected(0);
- }
- }
+ if (!loadMusicDeviceSetting(_mt32DevicePopUp, "mt32_device"))
+ _mt32DevicePopUp->setSelected(0);
// Native mt32 setting
_mt32Checkbox->setState(ConfMan.getBool("native_mt32", _domain));
@@ -630,7 +618,7 @@ void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &pr
Common::String context;
if (g_system->getOverlayWidth() <= 320)
context = "lowres";
-
+
// The GFX mode popup
_gfxPopUpDesc = new StaticTextWidget(boss, prefix + "grModePopupDesc", _("Graphics mode:"));
_gfxPopUp = new PopUpWidget(boss, prefix + "grModePopup");
@@ -663,7 +651,10 @@ void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &pr
void OptionsDialog::addAudioControls(GuiObject *boss, const Common::String &prefix) {
// The MIDI mode popup & a label
- _midiPopUpDesc = new StaticTextWidget(boss, prefix + "auMidiPopupDesc", _domain == Common::ConfigManager::kApplicationDomain ? _("Preferred Device:") : _("Music Device:"), _domain == Common::ConfigManager::kApplicationDomain ? _("Specifies preferred sound device or sound card emulator") : _("Specifies output sound device or sound card emulator"));
+ if (g_system->getOverlayWidth() > 320)
+ _midiPopUpDesc = new StaticTextWidget(boss, prefix + "auMidiPopupDesc", _domain == Common::ConfigManager::kApplicationDomain ? _("Preferred Device:") : _("Music Device:"), _domain == Common::ConfigManager::kApplicationDomain ? _("Specifies preferred sound device or sound card emulator") : _("Specifies output sound device or sound card emulator"));
+ else
+ _midiPopUpDesc = new StaticTextWidget(boss, prefix + "auMidiPopupDesc", _domain == Common::ConfigManager::kApplicationDomain ? _c("Preferred Dev.:", "lowres") : _c("Music Device:", "lowres"), _domain == Common::ConfigManager::kApplicationDomain ? _("Specifies preferred sound device or sound card emulator") : _("Specifies output sound device or sound card emulator"));
_midiPopUp = new PopUpWidget(boss, prefix + "auMidiPopup", _("Specifies output sound device or sound card emulator"));
// Populate it
@@ -676,7 +667,7 @@ void OptionsDialog::addAudioControls(GuiObject *boss, const Common::String &pref
const uint32 deviceGuiOption = MidiDriver::musicType2GUIO(d->getMusicType());
if ((_domain == Common::ConfigManager::kApplicationDomain && d->getMusicType() != MT_TOWNS // global dialog - skip useless FM-Towns, C64, Amiga, AppleIIGS options there
- && d->getMusicType() != MT_C64 && d->getMusicType() != MT_AMIGA && d->getMusicType() != MT_APPLEIIGS)
+ && d->getMusicType() != MT_C64 && d->getMusicType() != MT_AMIGA && d->getMusicType() != MT_APPLEIIGS && d->getMusicType() != MT_PC98)
|| (_domain != Common::ConfigManager::kApplicationDomain && !(_guioptions & allFlags)) // No flags are specified
|| (_guioptions & deviceGuiOption) // flag is present
// HACK/FIXME: For now we have to show GM devices, even when the game only has GUIO_MIDIMT32 set,
@@ -716,13 +707,25 @@ void OptionsDialog::addMIDIControls(GuiObject *boss, const Common::String &prefi
// Populate
const MusicPlugin::List p = MusicMan.getPlugins();
+ // Make sure the null device is the first one in the list to avoid undesired
+ // auto detection for users who don't have a saved setting yet.
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
- if (d->getMusicType() >= MT_GM || d->getMusicDriverId() == "auto") {
+ if (d->getMusicDriverId() == "null")
+ _gmDevicePopUp->appendEntry(_("Don't use General MIDI music"), d->getHandle());
+ }
+ }
+ // Now we add the other devices.
+ for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
+ MusicDevices i = (**m)->getDevices();
+ for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
+ if (d->getMusicType() >= MT_GM) {
if (d->getMusicType() != MT_MT32)
_gmDevicePopUp->appendEntry(d->getCompleteName(), d->getHandle());
- }
+ } else if (d->getMusicDriverId() == "auto") {
+ _gmDevicePopUp->appendEntry(_("Use first available device"), d->getHandle());
+ }
}
}
@@ -760,18 +763,29 @@ void OptionsDialog::addMT32Controls(GuiObject *boss, const Common::String &prefi
if (g_system->getOverlayWidth() > 320)
_mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _("True Roland MT-32 (disable GM emulation)"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer"));
else
- _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _c("True Roland MT-32 (disable GM emulation)", "lowres"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer"));
+ _mt32Checkbox = new CheckboxWidget(boss, prefix + "mcMt32Checkbox", _c("True Roland MT-32 (no GM emulation)", "lowres"), _("Check if you want to use your real hardware Roland-compatible sound device connected to your computer"));
// GS Extensions setting
_enableGSCheckbox = new CheckboxWidget(boss, prefix + "mcGSCheckbox", _("Enable Roland GS Mode"), _("Turns off General MIDI mapping for games with Roland MT-32 soundtrack"));
const MusicPlugin::List p = MusicMan.getPlugins();
+ // Make sure the null device is the first one in the list to avoid undesired
+ // auto detection for users who don't have a saved setting yet.
+ for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
+ MusicDevices i = (**m)->getDevices();
+ for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
+ if (d->getMusicDriverId() == "null")
+ _mt32DevicePopUp->appendEntry(_("Don't use Roland MT-32 music"), d->getHandle());
+ }
+ }
+ // Now we add the other devices.
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
- if (d->getMusicType() >= MT_GM || d->getMusicDriverId() == "auto") {
+ if (d->getMusicType() >= MT_GM)
_mt32DevicePopUp->appendEntry(d->getCompleteName(), d->getHandle());
- }
+ else if (d->getMusicDriverId() == "auto")
+ _mt32DevicePopUp->appendEntry(_("Use first available device"), d->getHandle());
}
}
@@ -795,7 +809,7 @@ void OptionsDialog::addSubtitleControls(GuiObject *boss, const Common::String &p
_subToggleSpeechOnly = new RadiobuttonWidget(boss, prefix + "subToggleSpeechOnly", _subToggleGroup, kSubtitlesSpeech, _("Speech"));
_subToggleSubOnly = new RadiobuttonWidget(boss, prefix + "subToggleSubOnly", _subToggleGroup, kSubtitlesSubs, _("Subtitles"));
_subToggleSubBoth = new RadiobuttonWidget(boss, prefix + "subToggleSubBoth", _subToggleGroup, kSubtitlesBoth, _("Both"));
-
+
_subSpeedDesc = new StaticTextWidget(boss, prefix + "subSubtitleSpeedDesc", _("Subtitle speed:"));
} else {
_subToggleDesc = new StaticTextWidget(boss, prefix + "subToggleDesc", _c("Text and Speech:", "lowres"));
@@ -805,7 +819,7 @@ void OptionsDialog::addSubtitleControls(GuiObject *boss, const Common::String &p
_subToggleSpeechOnly = new RadiobuttonWidget(boss, prefix + "subToggleSpeechOnly", _subToggleGroup, kSubtitlesSpeech, _("Spch"), _("Speech"));
_subToggleSubOnly = new RadiobuttonWidget(boss, prefix + "subToggleSubOnly", _subToggleGroup, kSubtitlesSubs, _("Subs"), _("Subtitles"));
_subToggleSubBoth = new RadiobuttonWidget(boss, prefix + "subToggleSubBoth", _subToggleGroup, kSubtitlesBoth, _c("Both", "lowres"), _("Show subtitles and play speech"));
-
+
_subSpeedDesc = new StaticTextWidget(boss, prefix + "subSubtitleSpeedDesc", _c("Subtitle speed:", "lowres"));
}
@@ -968,24 +982,39 @@ GlobalOptionsDialog::GlobalOptionsDialog()
//
// 5) The Paths tab
//
- tab->addTab(_("Paths"));
+ if (g_system->getOverlayWidth() > 320)
+ tab->addTab(_("Paths"));
+ else
+ tab->addTab(_c("Paths", "lowres"));
#if !( defined(__DC__) || defined(__GP32__) )
// These two buttons have to be extra wide, or the text will be
// truncated in the small version of the GUI.
// Save game path
- new ButtonWidget(tab, "GlobalOptions_Paths.SaveButton", _("Save Path: "), _("Specifies where your savegames are put"), kChooseSaveDirCmd);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GlobalOptions_Paths.SaveButton", _("Save Path:"), _("Specifies where your savegames are put"), kChooseSaveDirCmd);
+ else
+ new ButtonWidget(tab, "GlobalOptions_Paths.SaveButton", _c("Save Path:", "lowres"), _("Specifies where your savegames are put"), kChooseSaveDirCmd);
_savePath = new StaticTextWidget(tab, "GlobalOptions_Paths.SavePath", "/foo/bar", _("Specifies where your savegames are put"));
- new ButtonWidget(tab, "GlobalOptions_Paths.ThemeButton", _("Theme Path:"), 0, kChooseThemeDirCmd);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GlobalOptions_Paths.ThemeButton", _("Theme Path:"), 0, kChooseThemeDirCmd);
+ else
+ new ButtonWidget(tab, "GlobalOptions_Paths.ThemeButton", _c("Theme Path:", "lowres"), 0, kChooseThemeDirCmd);
_themePath = new StaticTextWidget(tab, "GlobalOptions_Paths.ThemePath", _c("None", "path"));
- new ButtonWidget(tab, "GlobalOptions_Paths.ExtraButton", _("Extra Path:"), _("Specifies path to additional data used by all games or ScummVM"), kChooseExtraDirCmd);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GlobalOptions_Paths.ExtraButton", _("Extra Path:"), _("Specifies path to additional data used by all games or ScummVM"), kChooseExtraDirCmd);
+ else
+ new ButtonWidget(tab, "GlobalOptions_Paths.ExtraButton", _c("Extra Path:", "lowres"), _("Specifies path to additional data used by all games or ScummVM"), kChooseExtraDirCmd);
_extraPath = new StaticTextWidget(tab, "GlobalOptions_Paths.ExtraPath", _c("None", "path"), _("Specifies path to additional data used by all games or ScummVM"));
#ifdef DYNAMIC_MODULES
- new ButtonWidget(tab, "GlobalOptions_Paths.PluginsButton", _("Plugins Path:"), 0, kChoosePluginsDirCmd);
+ if (g_system->getOverlayWidth() > 320)
+ new ButtonWidget(tab, "GlobalOptions_Paths.PluginsButton", _("Plugins Path:"), 0, kChoosePluginsDirCmd);
+ else
+ new ButtonWidget(tab, "GlobalOptions_Paths.PluginsButton", _c("Plugins Path:", "lowres"), 0, kChoosePluginsDirCmd);
_pluginsPath = new StaticTextWidget(tab, "GlobalOptions_Paths.PluginsPath", _c("None", "path"));
#endif
#endif
@@ -1010,7 +1039,7 @@ GlobalOptionsDialog::GlobalOptionsDialog()
_rendererPopUp->appendEntry(_(GUI::ThemeEngine::_rendererModes[i].name), GUI::ThemeEngine::_rendererModes[i].mode);
} else {
for (uint i = 1; i < GUI::ThemeEngine::_rendererModesSize; ++i)
- _rendererPopUp->appendEntry(_c(GUI::ThemeEngine::_rendererModes[i].name, "lowres"), GUI::ThemeEngine::_rendererModes[i].mode);
+ _rendererPopUp->appendEntry(_(GUI::ThemeEngine::_rendererModes[i].shortname), GUI::ThemeEngine::_rendererModes[i].mode);
}
if (g_system->getOverlayWidth() > 320)
@@ -1263,9 +1292,21 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
Common::String theme = browser.getSelected();
// FIXME: Actually, any changes (including the theme change) should
// only become active *after* the options dialog has closed.
+ Common::String lang = TransMan.getCurrentLanguage();
+ Common::String oldTheme = g_gui.theme()->getThemeId();
if (g_gui.loadNewTheme(theme)) {
- _curTheme->setLabel(g_gui.theme()->getThemeName());
- ConfMan.set("gui_theme", theme);
+ // If the charset has changed, it means the font were not found for the
+ // new theme. Since for the moment we do not support change of translation
+ // language without restarting, we let the user know about this.
+ if (lang != TransMan.getCurrentLanguage()) {
+ TransMan.setLanguage(lang.c_str());
+ g_gui.loadNewTheme(oldTheme);
+ MessageDialog error(_("The theme you selected does not support your current language. If you want to use this theme you need to switch to another language first."));
+ error.runModal();
+ } else {
+ _curTheme->setLabel(g_gui.theme()->getThemeName());
+ ConfMan.set("gui_theme", theme);
+ }
}
draw();
}
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index 92cea00f30..3a86c5092e 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -179,7 +179,7 @@ void SaveLoadChooser::reflowLayout() {
uint16 w, h;
if (!g_gui.xmlEval()->getWidgetData("SaveLoadChooser.Thumbnail", x, y, w, h))
- error("Error when loading position data for Save/Load Thumbnails.");
+ error("Error when loading position data for Save/Load Thumbnails");
int thumbW = kThumbnailWidth;
int thumbH = kThumbnailHeight2;
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index ea7ecd733c..1023e82ff9 100644
--- a/gui/themes/scummmodern.zip
+++ b/gui/themes/scummmodern.zip
Binary files differ
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 484dd15429..51f1ce6c6f 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -53,7 +53,7 @@
<def var = 'Tooltip.YDelta' value = '32'/>
<widget name = 'OptionsLabel'
- size = '110, Globals.Line.Height'
+ size = '115, Globals.Line.Height'
textalign = 'right'
/>
<widget name = 'SmallLabel'
@@ -344,7 +344,7 @@
<widget name = 'auPrefGmPopup'
type = 'PopUp'
/>
- </layout>
+ </layout>
<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '10' center = 'true'>
<widget name = 'mcFontButton'
type = 'Button'
@@ -712,7 +712,7 @@
<layout type = 'vertical' padding = '24, 24, 24, 24' center = 'true'>
<widget name = 'vcMuteCheckbox'
type = 'Checkbox'
- width = '80' <!-- FIXME: Why this is needed? -->
+ width = '120' <!-- FIXME: Why this is needed? -->
/>
</layout>
</layout>
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 5998c50b60..bd7b5fd8ea 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -483,7 +483,7 @@
</dialog>
<dialog name = 'GameOptions' overlays = 'screen' inset = '16' shading = 'dim'>
- <layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '16'>
+ <layout type = 'vertical' padding = '0, 0, 0, 0'>
<widget name = 'TabWidget'/>
<layout type = 'horizontal' padding = '8, 8, 8, 8'>
<space/>
diff --git a/gui/themes/scummtheme.py b/gui/themes/scummtheme.py
index 4a03c89799..e4e9549265 100755
--- a/gui/themes/scummtheme.py
+++ b/gui/themes/scummtheme.py
@@ -11,28 +11,28 @@ def buildTheme(themeName):
if not os.path.isdir(themeName) or not os.path.isfile(os.path.join(themeName, "THEMERC")):
print ("Invalid theme name: " + themeName)
return
-
+
zf = zipfile.ZipFile(themeName + ".zip", 'w')
-
+
print ("Building '" + themeName + "' theme:")
os.chdir(themeName)
-
+
zf.write('THEMERC', './THEMERC')
-
+
for filename in os.listdir('.'):
if os.path.isfile(filename) and not filename[0] == '.' and filename.endswith(THEME_FILE_EXTENSIONS):
zf.write(filename, './' + filename)
print (" Adding file: " + filename)
-
+
os.chdir('../')
-
+
zf.close()
def buildAllThemes():
for f in os.listdir('.'):
if os.path.isdir(os.path.join('.', f)) and not f[0] == '.':
buildTheme(f)
-
+
def parseSTX(theme_file, def_file):
comm = re.compile("<!--(.*?)-->", re.DOTALL)
head = re.compile("<\?(.*?)\?>")
@@ -40,33 +40,33 @@ def parseSTX(theme_file, def_file):
output = ""
for line in theme_file:
output += line.rstrip("\r\n\t ").lstrip() + " \n"
-
+
output = re.sub(comm, "", output)
output = re.sub(head, "", output)
output = output.replace("\t", " ").replace(" ", " ").replace("\"", "'")
output = output.replace(" = ", "=").replace(", ", ",")
-
+
for line in output.splitlines():
if line and not line.isspace():
def_file.write("\"" + line + "\"\n")
def buildDefTheme(themeName):
def_file = open("default.inc", "w")
-
+
if not os.path.isdir(themeName):
print ("Cannot open default theme dir.")
-
+
def_file.write(""" "<?xml version = '1.0'?>"\n""")
-
+
for filename in os.listdir(themeName):
filename = os.path.join(themeName, filename)
if os.path.isfile(filename) and filename.endswith(".stx"):
theme_file = open(filename, "r")
parseSTX(theme_file, def_file)
theme_file.close()
-
+
def_file.close()
-
+
def printUsage():
print ("===============================")
print ("ScummVM Theme Generation Script")
@@ -80,16 +80,16 @@ def printUsage():
print (" Creates a 'default.inc' file to embed the given theme in the source code.\n")
def main():
-
+
if len(sys.argv) == 2 and sys.argv[1] == "makeall":
buildAllThemes()
-
+
elif len(sys.argv) == 3 and sys.argv[1] == "make":
buildTheme(sys.argv[2])
-
+
elif len(sys.argv) == 3 and sys.argv[1] == "default":
buildDefTheme(sys.argv[2])
-
+
else:
printUsage()
diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat
index e00de3b9f3..b057f2ba44 100644
--- a/gui/themes/translations.dat
+++ b/gui/themes/translations.dat
Binary files differ
diff --git a/icons/scummvm.info b/icons/scummvm.info
index 97fbfa73bc..e30a7129ef 100644
--- a/icons/scummvm.info
+++ b/icons/scummvm.info
Binary files differ
diff --git a/po/POTFILES b/po/POTFILES
index 59af170c2f..2ebc0601e7 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -5,6 +5,7 @@ gui/browser.cpp
gui/chooser.cpp
gui/error.cpp
gui/GuiManager.cpp
+gui/KeysDialog.h
gui/KeysDialog.cpp
gui/launcher.cpp
gui/massadd.cpp
@@ -20,7 +21,13 @@ common/util.cpp
engines/dialogs.cpp
engines/scumm/dialogs.cpp
+engines/scumm/scumm.cpp
engines/mohawk/dialogs.cpp
+engines/mohawk/myst.cpp
+engines/mohawk/riven.cpp
+engines/cruise/menu.cpp
+engines/sci/engine/kfile.cpp
+engines/agos/saveload.cpp
sound/fmopl.cpp
sound/musicplugin.cpp
diff --git a/po/ca_ES.po b/po/ca_ES.po
index 695bb888ae..c2697ec28b 100644
--- a/po/ca_ES.po
+++ b/po/ca_ES.po
@@ -1,20 +1,20 @@
# Catalan translation for ScummVM.
-# Copyright (C) 2007-2010 ScummVM
+# Copyright (C) 2007-2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
# Jordi Vilalta Prat <jvprat@jvprat.com>, 2007-2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-06-26 16:45+0100\n"
-"Last-Translator: Jordi Vilalta Prat <jvprat@gmail.com>\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-09-21 23:12+0100\n"
+"Last-Translator: Jordi Vilalta Prat <jvprat@jvprat.com>\n"
"Language-Team: Catalan <scummvm-devel@lists.sf.net>\n"
+"Language: Catalan\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Catalan\n"
#: gui/about.cpp:96
#, c-format
@@ -29,47 +29,56 @@ msgstr "Característiques compilades:"
msgid "Available engines:"
msgstr "Motors disponibles:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "Amunt"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "Torna al nivell de directoris anterior"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "Amunt"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "Cancel·la"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "Escull"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "Tanca"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "Clic del ratolí"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "Mostra el teclat"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "Remapeja les tecles"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "Sel·leccioneu una acció a mapejar"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "Mapeja"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -97,19 +106,15 @@ msgstr "Seleccioneu una acció"
msgid "Press the key to associate"
msgstr "Premeu la tecla a associar"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "Sel·leccioneu una acció per mapejar"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "Joc"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "Identificador:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -117,19 +122,29 @@ msgstr ""
"Identificador de joc curt utilitzat per referir-se a les partides i per "
"executar el joc des de la línia de comandes"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "Nom:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "Títol complet del joc"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "Nom:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Idioma:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -137,281 +152,285 @@ msgstr ""
"Idioma del joc. Això no convertirà la vostra versió Espanyola del joc a "
"Anglès"
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<per defecte>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "Plataforma:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "Plataforma per la que el joc es va dissenyar originalment"
-#: gui/launcher.cpp:197
-#, fuzzy
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
-msgstr "Plataforma:"
+msgstr "Platafor.:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Gràfics"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "GFX"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "Fer canvis sobre les opcions globals de gràfics"
-#: gui/launcher.cpp:214
-#, fuzzy
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
-msgstr "Fer canvis sobre les opcions globals de gràfics"
+msgstr "Canviar les opcions de gràfics"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Àudio"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "Fer canvis sobre les opcions globals d'àudio"
-#: gui/launcher.cpp:226
-#, fuzzy
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
-msgstr "Fer canvis sobre les opcions globals d'àudio"
+msgstr "Canviar les opcions d'àudio"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Volum"
-#: gui/launcher.cpp:237 gui/options.cpp:951
-#, fuzzy
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "Volum"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "Fer canvis sobre les opcions globals de volum"
-#: gui/launcher.cpp:242
-#, fuzzy
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
-msgstr "Fer canvis sobre les opcions globals de volum"
+msgstr "Canviar les opcions de volum"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "Fer canvis sobre les opcions globals de MIDI"
-#: gui/launcher.cpp:254
-#, fuzzy
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
-msgstr "Fer canvis sobre les opcions globals de MIDI"
+msgstr "Canviar les opcions de MIDI"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
-msgstr ""
+msgstr "MT-32"
-#: gui/launcher.cpp:267
-#, fuzzy
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
-msgstr "Fer canvis sobre les opcions globals de MIDI"
+msgstr "Fer canvis sobre les opcions globals de MT-32"
-#: gui/launcher.cpp:269
-#, fuzzy
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
-msgstr "Fer canvis sobre les opcions globals de MIDI"
+msgstr "Canviar les opcions de MT-32"
+
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "Camins"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr "Camins"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
msgid "Game Path:"
-msgstr "Camí del Joc:"
+msgstr "Camí del joc:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:297
+msgctxt "lowres"
+msgid "Game Path:"
+msgstr "Camí joc:"
+
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
-msgstr "Camí Extra:"
+msgstr "Camí extra:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "Especifica el camí de dades addicionals utilitzades pel joc"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Camí extra:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
-msgstr "Camí de les Partides:"
+msgstr "Camí de partides:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "Especifica on es desaran les partides"
-#: gui/launcher.cpp:296
-#, fuzzy
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
-msgstr "Camí de les Partides:"
-
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
-#, fuzzy
+msgstr "Partides:"
+
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "Cap"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "Per defecte"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "Seleccioneu el fitxer SoundFont"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "Seleccioneu el directori amb les dades del joc"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "Seleccioneu el directori addicional del joc"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "Seleccioneu el directori de les partides desades"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr ""
-"Aquest identificador de joc ja està usat. Si us plau, trieu-ne un altre."
+"Aquest identificador de joc ja està en ús. Si us plau, trieu-ne un altre."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~T~anca"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "Surt de ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "~Q~uant a..."
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "Quant a ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~O~pcions..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "Canvia les opcions globals de ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "~I~nicia"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "Iniciant el joc seleccionat"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~C~arrega..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "Carrega una partida pel joc seleccionat"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~A~fegeix Joc..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr "Mantingueu premut Shift per a l'Addició Massiva"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "~E~dita Joc..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "Canvia les opcions del joc"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "~S~uprimeix Joc"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr ""
"Elimina un joc de la llista. Els fitxers de dades del joc es mantenen "
"intactes"
-#: gui/launcher.cpp:563
-#, fuzzy
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr "~A~fegeix Joc..."
-#: gui/launcher.cpp:565
-#, fuzzy
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr "~E~dita Joc..."
-#: gui/launcher.cpp:567
-#, fuzzy
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
-msgstr "~S~uprimeix Joc"
+msgstr "~S~uprimeix"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "Cerca a la llista de jocs"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "Cerca:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "Neteja el valor"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "Carrega partida:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "Carrega"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -419,64 +438,62 @@ msgstr ""
"Esteu segur que voleu executar el detector massiu de jocs? Això pot afegir "
"una gran quantitat de jocs."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "Sí"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "No"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "ScummVM no ha pogut obrir el directori especificat!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "ScummVM no ha pogut trobar cap joc al directori especificat!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "Seleccioneu el joc:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "Realment voleu suprimir la configuració d'aquest joc?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr "Aquest joc no suporta la càrrega de partides des del llançador."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr ""
"ScummVM no ha pogut trobar cap motor capaç d'executar el joc seleccionat!"
-#: gui/launcher.cpp:1044
-#, fuzzy
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
-msgstr "Addició Massiva..."
+msgstr "Afegeix Jocs"
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "Addició Massiva..."
-#: gui/launcher.cpp:1045
-#, fuzzy
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr "Afegeix Joc..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "Afegeix Joc..."
@@ -491,7 +508,7 @@ msgstr "S'ha acabat la cerca!"
#: gui/massadd.cpp:247
#, c-format
msgid "Discovered %d new games."
-msgstr "S'han descobert %d jocs nous."
+msgstr "S'han trobat %d jocs nous."
#: gui/massadd.cpp:251
#, c-format
@@ -501,7 +518,7 @@ msgstr "S'han cercat %d directoris ..."
#: gui/massadd.cpp:254
#, c-format
msgid "Discovered %d new games ..."
-msgstr "S'han descobert %d jocs nous ..."
+msgstr "S'han trobat %d jocs nous ..."
#: gui/options.cpp:78
msgid "Never"
@@ -544,8 +561,7 @@ msgid "48 kHz"
msgstr "48 kHz"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
-#, fuzzy
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "Cap"
@@ -568,42 +584,51 @@ msgstr "Mode pantalla completa"
#: gui/options.cpp:659
msgid "Aspect ratio correction"
-msgstr "Correcció del rati d'aspecte"
+msgstr "Correcció de la relació d'aspecte"
#: gui/options.cpp:659
msgid "Correct aspect ratio for 320x200 games"
msgstr "Corregeix la relació d'aspecte per jocs de 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
-msgstr "Dispositiu Preferit:"
+msgstr "Disp. preferit:"
-#: gui/options.cpp:666
-#, fuzzy
+#: gui/options.cpp:667
msgid "Music Device:"
-msgstr "Dispositiu GM:"
+msgstr "Disp. de música:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr "Especifica el dispositiu de so o l'emulador de tarja de so preferit"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr "Especifica el dispositiu de so o l'emulador de tarja de so de sortida"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "Disp. preferit:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Disp. de música:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
-msgstr "Emulador d'AdLib:"
+msgstr "Emulador AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "AdLib s'utilitza per la música de molts jocs"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
-msgstr "Freqüència de sortida:"
+msgstr "Freq. sortida:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -611,57 +636,55 @@ msgstr ""
"Valors més alts especifiquen millor qualitat de so però pot ser que la "
"vostra tarja de so no ho suporti"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "Dispositiu GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr ""
"Especifica el dispositiu de so per defecte per a la sortida General MIDI"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "Fitxer SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr "Algunes targes de so, Fluidsynth i Timidity suporten SoundFont"
-#: gui/options.cpp:738
-#, fuzzy
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
-msgstr "Fitxer SoundFont:"
+msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "Mode combinat AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "Utilitza MIDI i la generació de so AdLib alhora"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "Guany MIDI:"
-#: gui/options.cpp:756
-#, fuzzy
+#: gui/options.cpp:759
msgid "MT-32 Device:"
-msgstr "Dispositiu MT32:"
+msgstr "Disposit. MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"Especifica el dispositiu de so per defecte per a la sortida de Roland MT-32/"
"LAPC1/CM32l/CM64"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Roland MT-32 real (desactiva l'emulació GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -669,194 +692,199 @@ msgstr ""
"Marqueu si voleu utilitzar el vostre dispositiu hardware real de so "
"compatible amb Roland connectat al vostre ordinador"
-#: gui/options.cpp:763
-#, fuzzy
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
-msgstr "Roland MT-32 real (desactiva l'emulació GM)"
+msgid "True Roland MT-32 (no GM emulation)"
+msgstr "Roland MT-32 real (sense emulació GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Activa el Mode Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
"Desactiva la conversió General MIDI pels jocs que tenen banda sonora per a "
"Roland MT-32"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Text i Veus:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr "Veus"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "Subtítols"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "Ambdós"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
-msgstr "Velocitat dels subtítols:"
+msgstr "Velocitat de subt.:"
-#: gui/options.cpp:801
-#, fuzzy
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Text i Veus:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr "Veus"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "Subt"
-#: gui/options.cpp:807
-#, fuzzy
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr "Ambdós"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "Mostra els subtítols i reprodueix la veu"
-#: gui/options.cpp:809
-#, fuzzy
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
-msgstr "Velocitat dels subtítols:"
+msgstr "Veloc. de subt.:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
-msgstr "Volum de la música:"
+msgstr "Volum de música:"
-#: gui/options.cpp:827
-#, fuzzy
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
-msgstr "Volum de la música:"
+msgstr "Volum de música:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Silenciar tot"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
-msgstr "Volum dels efectes:"
+msgstr "Volum d'efectes:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "Volum dels sons d'efectes especials"
-#: gui/options.cpp:839
-#, fuzzy
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
-msgstr "Volum dels efectes:"
+msgstr "Volum d'efectes:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
-msgstr "Volum de la veu:"
+msgstr "Volum de veus:"
-#: gui/options.cpp:849
-#, fuzzy
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
-msgstr "Volum de la veu:"
+msgstr "Volum de veus:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "Camí de les Partides: "
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "Camí dels temes:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
-msgstr "Camí dels Temes:"
+msgstr "Camí temes:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
"Especifica el camí de les dades addicionals utilitzades per tots els jocs o "
"pel ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
msgid "Plugins Path:"
msgstr "Camí dels connectors:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1006
+msgctxt "lowres"
+msgid "Plugins Path:"
+msgstr "Camí de connectors:"
+
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "Misc"
-#: gui/options.cpp:999
-#, fuzzy
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "Misc"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Tema:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
-msgstr "Mode de pintat de la interfície d'usuari:"
+msgstr "Pintat GUI:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Desat automàtic:"
-#: gui/options.cpp:1019
-#, fuzzy
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
-msgstr "Desat automàtic:"
+msgstr "Auto-desat:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Tecles"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
-msgstr "Idioma de la interfície d'usuari:"
+msgstr "Idioma GUI:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Idioma de la interfície d'usuari de ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr "Heu de reiniciar ScummVM perquè tots els canvis tingui efecte."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "Seleccioneu el directori de les partides desades"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
"No es pot escriure al directori seleccionat. Si us plau, escolliu-ne un "
"altre."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
-msgstr "Seleccioneu el directori dels temes de la Interfície d'Usuari"
+msgstr "Seleccioneu el directori dels temes"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "Seleccioneu el directori dels fitxers extra"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "Seleccioneu el directori dels connectors"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+"El tema que heu seleccionat no suporta l'idioma actual. Si voleu utilitzar "
+"aquest tema primer haureu de canviar a un altre idioma."
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr "No hi ha data desada"
@@ -897,30 +925,31 @@ msgstr "Partida sense títol"
msgid "Select a Theme"
msgstr "Seleccioneu un Tema"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "GFX desactivats"
-#: gui/ThemeEngine.cpp:335
-msgid "Standard Renderer (16bpp)"
-msgstr "Pintat estàndard (16bpp)"
-
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
-msgstr "Pintat amb antialias (16bpp)"
-
-#: gui/ThemeEngine.cpp:341
-#, fuzzy
+#: gui/ThemeEngine.cpp:332
msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "GFX desactivats"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "Pintat estàndard (16bpp)"
-#: gui/ThemeEngine.cpp:342
-#, fuzzy
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
+msgstr "Estàndard (16bpp)"
+
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr "Pintat amb antialias (16bpp)"
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr "Amb antialias (16bpp)"
+
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
@@ -945,11 +974,11 @@ msgstr "Pausa"
msgid "Skip line"
msgstr "Salta la línia"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "Error al executar el joc:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr "No s'ha pogut trobar cap motor capaç d'executar el joc seleccionat"
@@ -1014,13 +1043,11 @@ msgid "Hercules Amber"
msgstr "Hercules Àmbar"
#: common/util.cpp:262
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Green"
msgstr "Hercules Verd"
#: common/util.cpp:263
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Amber"
msgstr "Hercules Àmbar"
@@ -1054,16 +1081,18 @@ msgid "~R~eturn to Launcher"
msgstr "~R~etorna al Llançador"
#: engines/dialogs.cpp:112
-#, fuzzy
msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "~R~etorna al Llançador"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "Desa la partida:"
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1098,6 +1127,39 @@ msgstr "~S~egüent"
msgid "~C~lose"
msgstr "~T~anca"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"No s'ha pogut desar l'estat del joc al fitxer:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"No s'ha pogut carregar l'estat del joc del fitxer:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"S'ha desat l'estat del joc en el fitxer:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "Mode ~Z~ip activat"
@@ -1110,22 +1172,29 @@ msgstr "~T~ransicions activades"
msgid "~W~ater Effect Enabled"
msgstr "~E~fecte de l'aigua activat"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "Desa la partida:"
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "Restaura"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "Emulador OPL de MAME"
#: sound/fmopl.cpp:53
msgid "DOSBox OPL emulator"
-msgstr "Emulador OPL de DOSBox"
+msgstr "Emulador OPL DOSBox"
#: sound/null.h:45
msgid "No music"
msgstr "Sense música"
#: sound/mods/paula.cpp:192
-#, fuzzy
msgid "Amiga Audio Emulator"
-msgstr "Emulador d'AdLib"
+msgstr "Emulador d'àudio Amiga"
#: sound/softsynth/adlib.cpp:1590
msgid "AdLib Emulator"
@@ -1133,12 +1202,11 @@ msgstr "Emulador d'AdLib"
#: sound/softsynth/appleiigs.cpp:36
msgid "Apple II GS Emulator (NOT IMPLEMENTED)"
-msgstr ""
+msgstr "Emulador d'Apple II GS (NO IMPLEMENTAT)"
#: sound/softsynth/sid.cpp:1434
-#, fuzzy
msgid "C64 Audio Emulator"
-msgstr "Emulador d'AdLib"
+msgstr "Emulador d'àudio C64"
#: sound/softsynth/mt32.cpp:327
msgid "Initialising MT-32 Emulator"
@@ -1150,7 +1218,7 @@ msgstr "Emulador de MT-32"
#: sound/softsynth/pcspk.cpp:142
msgid "PC Speaker Emulator"
-msgstr "Emulador d'Altaveu de PC"
+msgstr "Emulador Altaveu PC"
#: sound/softsynth/pcspk.cpp:161
msgid "IBM PCjr Emulator"
@@ -1198,7 +1266,7 @@ msgstr "Mostra el cursor del ratolí"
#: backends/platform/ds/arm9/source/dsoptions.cpp:69
msgid "Snap to edges"
-msgstr ""
+msgstr "Enganxa a les vores"
#: backends/platform/ds/arm9/source/dsoptions.cpp:71
msgid "Touch X Offset"
@@ -1242,7 +1310,7 @@ msgstr "Sense escalar (haureu de desplaçar-vos a esquerra i dreta)"
#: backends/platform/ds/arm9/source/dsoptions.cpp:114
msgid "Brightness:"
-msgstr "Brillantor:"
+msgstr "Lluminositat:"
#: backends/platform/ds/arm9/source/dsoptions.cpp:124
msgid "High quality audio (slower) (reboot)"
@@ -1252,11 +1320,11 @@ msgstr "Alta qualitat d'àudio (més lent) (reiniciar)"
msgid "Disable power off"
msgstr "Desactiva l'apagat automàtic"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "Mode Touchpad activat."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "Mode Touchpad desactivat."
@@ -1267,10 +1335,9 @@ msgid "Normal (no scaling)"
msgstr "Normal (sense escalar)"
#: backends/platform/sdl/graphics.cpp:59
-#, fuzzy
msgctxt "lowres"
msgid "Normal (no scaling)"
-msgstr "Normal (sense escalar)"
+msgstr "Normal (no escalat)"
#: backends/platform/symbian/src/SymbianActions.cpp:41
#: backends/platform/wince/CEActionsSmartphone.cpp:38
@@ -1348,7 +1415,7 @@ msgstr "Teclat virtual"
msgid "Key mapper"
msgstr "Mapejador de tecles"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "Vols sortir?"
@@ -1362,15 +1429,15 @@ msgstr "Mode de vídeo actual:"
#: backends/platform/wii/options.cpp:56
msgid "Double-strike"
-msgstr ""
+msgstr "Double-strike"
#: backends/platform/wii/options.cpp:60
msgid "Horizontal underscan:"
-msgstr ""
+msgstr "Underscan horitzontal:"
#: backends/platform/wii/options.cpp:66
msgid "Vertical underscan:"
-msgstr ""
+msgstr "Underscan vertical:"
#: backends/platform/wii/options.cpp:71
msgid "Input"
@@ -1474,11 +1541,11 @@ msgstr "Iniciant la xarxa"
#: backends/platform/wii/options.cpp:182
msgid "Timeout while initialising network"
-msgstr ""
+msgstr "S'ha excedit el temps d'iniciació de la xarxa"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "Xarxa no iniciada (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1542,7 +1609,7 @@ msgstr "Voleu carregar o desar el joc?"
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
msgid " Are you sure you want to quit ? "
-msgstr ""
+msgstr " Esteu segur de voler sortir? "
#: backends/platform/wince/CEActionsSmartphone.cpp:49
msgid "Keyboard"
@@ -1558,98 +1625,37 @@ msgstr "Utilitzant el controlador SDL "
#: backends/platform/wince/CELauncherDialog.cpp:62
msgid "Display "
-msgstr "Pantalla"
+msgstr "Pantalla "
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Do you want to perform an automatic scan ?"
msgstr "Voleu fer una cerca automàtica?"
-#, fuzzy
-#~ msgctxt "lowres"
-#~ msgid "Special sound effects volume"
-#~ msgstr "Volum dels sons d'efectes especials"
-
-#~ msgid "English"
-#~ msgstr "Anglès"
-
-#~ msgid "Failed to load any GUI theme, aborting"
-#~ msgstr "No s'ha pogut carregar cap tema de la interfície d'usuari, avortant"
-
-#, fuzzy
-#~ msgid "User picked target '%s' (gameid '%s')...\n"
-#~ msgstr ""
-#~ "L'usuari ha seleccionat el target '%s' (identificador de joc '%s')...\n"
-
-#~ msgid " Looking for a plugin supporting this gameid... "
-#~ msgstr " Cercant un connector que suporti aquest identificador de joc... "
-
-#~ msgid "failed\n"
-#~ msgstr "ha fallat\n"
-
-#~ msgid ""
-#~ "%s is an invalid gameid. Use the --list-games option to list supported "
-#~ "gameid"
-#~ msgstr ""
-#~ "%s és un identificador de joc invàlid. Utilitzeu l'opció --list-games per "
-#~ "llistar els identificadors de joc suportats"
-
-#~ msgid " Starting '%s'\n"
-#~ msgstr " Iniciant '%s'\n"
-
-#, fuzzy
-#~ msgid "%s failed to instantiate engine: %s (target '%s', path '%s')"
-#~ msgstr "%s ha fallat l'iniciat del motor: %s (target '%s', camí '%s')"
-
-#~ msgid "Chinese (Taiwan)"
-#~ msgstr "Xinès (Taiwan)"
-
-#~ msgid "Czech"
-#~ msgstr "Txec"
-
-#~ msgid "Dutch"
-#~ msgstr "Danès"
-
-#~ msgid "English (GB)"
-#~ msgstr "Anglès (GB)"
-
-#~ msgid "English (US)"
-#~ msgstr "Anglès (EUA)"
-
-#~ msgid "French"
-#~ msgstr "Francès"
-
-#~ msgid "German"
-#~ msgstr "Alemany"
-
-#~ msgid "Hebrew"
-#~ msgstr "Hebreu"
-
-#~ msgid "Italian"
-#~ msgstr "Italià"
-
-#~ msgid "Japanese"
-#~ msgstr "Japonès"
-
-#~ msgid "Korean"
-#~ msgstr "Coreà"
-
-#~ msgid "Polish"
-#~ msgstr "Polac"
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr ""
-#~ msgid "Portuguese"
-#~ msgstr "Portuguès"
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
-#~ msgid "Russian"
-#~ msgstr "Rus"
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
-#~ msgid "Spanish"
-#~ msgstr "Espanyol"
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
-#~ msgid "Swedish"
-#~ msgstr "Suís"
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
-#~ msgid "Failed to load MT32_CONTROL.ROM"
-#~ msgstr "No s'ha pogut carregar el fitxer MT32_CONTROL.ROM"
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
-#~ msgid "Failed to load MT32_PCM.ROM"
-#~ msgstr "No s'ha pogut carregar el fitxer MT32_PCM.ROM"
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
diff --git a/po/de_DE.po b/po/de_DE.po
index a6db88eedb..4445ad4d36 100644
--- a/po/de_DE.po
+++ b/po/de_DE.po
@@ -1,14 +1,14 @@
-# LANGUAGE translation for ScummVM.
+# German translation for ScummVM.
# Copyright (C) 2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+# Lothar Serra Mari <Lothar@Windowsbase.de> & Simon Sawatzki <SimSaw@gmx.de>, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-09-02 16:01+0100\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-10-01 17:37+0100\n"
"Last-Translator: Simon Sawatzki <SimSaw@gmx.de>\n"
"Language-Team: Lothar Serra Mari <Lothar@Windowsbase.de> & Simon Sawatzki "
"<SimSaw@gmx.de>\n"
@@ -31,47 +31,56 @@ msgstr "Verwendete Funktionen:"
msgid "Available engines:"
msgstr "Verfügbare Spiele-Engines:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "Pfad hoch"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "Zu höherer Pfadebene wechseln"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "Pfad hoch"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "Abbrechen"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "Auswählen"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "Schließen"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "Mausklick"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "Tastatur anzeigen"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "Tasten neu zuweisen"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "Eine Aktion zum Zuweisen auswählen"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "Zuweisen"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -99,19 +108,15 @@ msgstr "Bitte eine Aktion auswählen"
msgid "Press the key to associate"
msgstr "Taste drücken, um sie zuzuweisen"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "Eine Aktion zum Zuweisen auswählen"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "Spiel"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "Kennung:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -119,19 +124,29 @@ msgstr ""
"Kurzer Spielname, um die Spielstände zuzuordnen und das Spiel von der "
"Kommandozeile aus starten zu können"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "Name:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "Voller Name des Spiels"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "Name:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Sprache:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -139,267 +154,284 @@ msgstr ""
"Sprache des Spiels. Diese Funktion wird nicht eine spanische Version des "
"Spiels in eine deutsche verwandeln."
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<Standard>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "Plattform:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "Plattform, für die das Spiel ursprünglich erstellt wurde"
-#: gui/launcher.cpp:197
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr "Plattform:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Grafik"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "GFX"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "Globale Grafikeinstellungen übergehen"
-#: gui/launcher.cpp:214
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr "Globale Grafikeinstellungen übergehen"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Audio"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "Globale Audioeinstellungen übergehen"
-#: gui/launcher.cpp:226
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr "Globale Audioeinstellungen übergehen"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Lautstärke"
-#: gui/launcher.cpp:237 gui/options.cpp:951
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "Lautst."
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "Globale Lautstärke-Einstellungen übergehen"
-#: gui/launcher.cpp:242
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr "Globale Lautstärkeeinstellungen übergehen"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "Globale MIDI-Einstellungen übergehen"
-#: gui/launcher.cpp:254
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr "Globale MIDI-Einstellungen übergehen"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr "MT-32"
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr "Globale MT-32-Einstellungen übergehen"
-#: gui/launcher.cpp:269
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr "Globale MT-32-Einstellungen übergehen"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "Pfade"
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr "Pfade"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
msgid "Game Path:"
msgstr "Spielpfad:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:297
+msgctxt "lowres"
+msgid "Game Path:"
+msgstr "Spielpfad:"
+
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "Extrapfad:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "Legt das Verzeichnis für zusätzliche Spieldateien fest."
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Extrapfad:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "Spielstände:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "Legt fest, wo die Spielstände abgelegt werden."
-#: gui/launcher.cpp:296
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "Speichern:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "Keiner"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "Standard"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "SoundFont auswählen"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "Verzeichnis mit Spieldateien auswählen"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "Verzeichnis mit zusätzlichen Dateien auswählen"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "Verzeichnis für Spielstände auswählen"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "Diese Spielkennung ist schon vergeben. Bitte eine andere wählen."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~B~eenden"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "ScummVM beenden"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "Übe~r~"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "Über ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~O~ptionen"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "Globale ScummVM-Einstellungen bearbeiten"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "~S~tarten"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "Ausgewähltes Spiel starten"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~L~aden..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "Spielstand für ausgewähltes Spiel laden"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "Spiel ~h~inzufügen"
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr ""
"Umschalttaste (Shift) gedrückt halten, um Verzeichnisse nach Spielen zu "
"durchsuchen"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "Spielo~p~tionen"
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "Spieloptionen ändern"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "Spiel ~e~ntfernen"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr "Spiel aus der Liste entfernen. Die Spieldateien bleiben erhalten."
-#: gui/launcher.cpp:563
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr "~H~inzufügen"
-#: gui/launcher.cpp:565
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr "Spielo~p~tion"
-#: gui/launcher.cpp:567
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr "~E~ntfernen"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "In Spieleliste suchen"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "Suchen:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "Wert löschen"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "Spiel laden:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "Laden"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -407,62 +439,62 @@ msgstr ""
"Möchten Sie wirklich den PC nach Spielen durchsuchen? Möglicherweise wird "
"dabei eine größere Menge an Spielen hinzugefügt."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "Ja"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "Nein"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
-msgstr "ScummVM kann das gewählte Verzeichnis nicht öffnen!"
+msgstr "ScummVM konnte das gewählte Verzeichnis nicht öffnen!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
-msgstr "ScummVM kann in dem gewählten Verzeichnis kein Spiel finden!"
+msgstr "ScummVM konnte im gewählten Verzeichnis kein Spiel finden!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "Spiel auswählen:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "Möchten Sie wirklich diese Spielkonfiguration entfernen?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr ""
"Für dieses Spiel wird das Laden aus der Spieleliste heraus nicht unterstützt."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr "ScummVM konnte keine Engine finden, um das Spiel zu starten!"
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr "Durchsuchen"
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "Durchsuchen"
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr "Hinzufügen"
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "Spiel hinzufügen"
@@ -530,7 +562,7 @@ msgid "48 kHz"
msgstr "48 kHz"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "-"
@@ -560,36 +592,46 @@ msgstr "Seitenverhältnis korrigieren"
msgid "Correct aspect ratio for 320x200 games"
msgstr "Seitenverhältnis für Spiele mit der Auflösung 320x200 korrigieren"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
msgstr "Standard-Gerät:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Music Device:"
msgstr "Musikgerät:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
"Legt das bevorzugte Tonwiedergabe-Gerät oder den Soundkarten-Emulator fest."
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr "Legt das Musikwiedergabe-Gerät oder den Soundkarten-Emulator fest."
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "Standard-Gerät:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Musikgerät:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "AdLib-Emulator"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "AdLib wird für die Musik in vielen Spielen verwendet."
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "Ausgabefrequenz:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -597,56 +639,56 @@ msgstr ""
"Höhere Werte bewirken eine bessere Soundqualität, werden aber möglicherweise "
"nicht von jeder Soundkarte unterstützt."
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "GM-Gerät:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr ""
"Legt das standardmäßige Musikwiedergabe-Gerät für General-MIDI-Ausgabe fest."
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
"SoundFont wird von einigen Soundkarten, Fluidsynth und Timidity unterstützt."
-#: gui/options.cpp:738
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "AdLib-/MIDI-Modus"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "Benutzt MIDI und AdLib zur Sounderzeugung."
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "MIDI-Lautstärke:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "MT-32 Device:"
msgstr "MT-32-Gerät:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"Legt das standardmäßige Tonwiedergabe-Gerät für die Ausgabe von Roland MT-32/"
"LAPC1/CM32l/CM64 fest."
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Echte Roland-MT-32-Emulation (GM-Emulation deaktiviert)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -654,185 +696,197 @@ msgstr ""
"Wählen Sie dies aus, wenn Sie Ihre echte Hardware, die mit einer Roland-"
"kompatiblen Soundkarte verbunden ist, verwenden möchten."
-#: gui/options.cpp:763
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr "Echte Roland-MT-32-Emulation (kein GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Roland-GS-Modus"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
"Schaltet die General-MIDI-Zuweisung für Spiele mit Roland-MT-32-Audiospur "
"aus."
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Sprache und Text:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr "Sprache"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "Untertitel"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "Beides"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr "Untertitel-Tempo:"
-#: gui/options.cpp:801
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Sprache + Text:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr "Spr."
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "TXT"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr "S+T"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "Untertitel anzeigen und Sprachausgabe aktivieren"
-#: gui/options.cpp:809
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "Text-Tempo:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "Musiklautstärke:"
-#: gui/options.cpp:827
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr "Musiklautstärke:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Alles aus"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "Effektlautstärke:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "Lautstärke spezieller Soundeffekte"
-#: gui/options.cpp:839
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr "Effektlautst.:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr "Sprachlautstärke:"
-#: gui/options.cpp:849
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
msgstr "Sprachlautst.:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "Spielstände: "
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "Themenpfad:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr "Themenpfad:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
"Legt das Verzeichnis für zusätzliche Spieldateien für alle Spiele in ScummVM "
"fest."
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
msgid "Plugins Path:"
msgstr "Plugin-Pfad:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1006
+msgctxt "lowres"
+msgid "Plugins Path:"
+msgstr "Plugin-Pfad:"
+
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "Sonstiges"
-#: gui/options.cpp:999
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "Andere"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Thema:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr "GUI-Renderer:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Autom. Speichern:"
-#: gui/options.cpp:1019
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
-msgstr "Autom. Sich.:"
+msgstr "Speich.(auto)"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Tasten"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
-msgstr "GUI-Sprache:"
+msgstr "Sprache:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Sprache der ScummVM-Oberfläche"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
-msgstr "Sie müssen ScummVM neustarten, um die Einstellungen zu übernehmen."
+msgstr "Sie müssen ScummVM neu starten, um die Einstellungen zu übernehmen."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "Verzeichnis für Spielstände auswählen"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
"In das gewählte Verzeichnis kann nicht geschrieben werden. Bitte ein anderes "
"auswählen."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr "Verzeichnis für Oberflächen-Themen"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "Verzeichnis für zusätzliche Dateien auswählen"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "Verzeichnis für Erweiterungen auswählen"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr "Kein Datum gespeichert"
@@ -873,32 +927,35 @@ msgstr "Unbenannt"
msgid "Select a Theme"
msgstr "Thema auswählen"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
-msgstr "GFX ausgeschalten"
+msgstr "GFX ausgeschaltet"
-#: gui/ThemeEngine.cpp:335
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "GFX ausgeschaltet"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "Standard-Renderer (16bpp)"
-#: gui/ThemeEngine.cpp:337
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
+msgstr "Standard (16bpp)"
+
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr "Kantenglättung (16bpp)"
-#: gui/ThemeEngine.cpp:341
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
-msgstr "Standard-Renderer(16bpp)"
-
-#: gui/ThemeEngine.cpp:342
-msgctxt "lowres"
-msgid "Antialiased Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
msgstr "Kantenglättung (16bpp)"
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
-msgstr "Engine unterstützt den Debug-Level \"%s\" nicht"
+msgstr "Engine unterstützt den Debug-Level \"%s\" nicht."
#: base/main.cpp:273
msgid "Menu"
@@ -919,13 +976,13 @@ msgstr "Pause"
msgid "Skip line"
msgstr "Zeile überspringen"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "Fehler beim Ausführen des Spiels:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
-msgstr "Kann keine Spiel-Engine finden, die dieses Spiel starten kann."
+msgstr "Konnte keine Spiel-Engine finden, die dieses Spiel starten kann."
#: common/error.cpp:43
msgid "Invalid Path"
@@ -1030,11 +1087,14 @@ msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "Zur Spiele~l~iste"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "Speichern:"
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1069,6 +1129,39 @@ msgstr "~W~eiter"
msgid "~C~lose"
msgstr "~S~chließen"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"Konnte Spielstand nicht in folgender Datei speichern:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"Konnte Spielstand nicht aus folgender Datei laden:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"Spielstand erfolgreich in folgender Datei gespeichert:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "~Z~ip-Modus aktiviert"
@@ -1081,6 +1174,14 @@ msgstr "Über~g~änge aktiviert"
msgid "~W~ater Effect Enabled"
msgstr "~W~assereffekt aktiviert"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "Spiel laden:"
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "Laden"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "MAME-OPL-Emulator"
@@ -1221,11 +1322,11 @@ msgstr "Hohe Audioqualität (lansamer) (erfordert Neustart)"
msgid "Disable power off"
msgstr "Stromsparmodus abschalten"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "Touchpad-Modus aktiviert."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "Touchpad-Modus ausgeschaltet."
@@ -1316,7 +1417,7 @@ msgstr "Virtuelle Tastatur"
msgid "Key mapper"
msgstr "Tasten zuordnen"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "Möchten Sie beenden?"
@@ -1446,7 +1547,7 @@ msgstr "Zeitüberschreitung beim Starten des Netzwerks"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "Netzwerk nicht gestartet (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1532,7 +1633,68 @@ msgstr "Anzeige"
msgid "Do you want to perform an automatic scan ?"
msgstr "Möchten Sie eine automatische Durchsuchung vornehmen?"
-#, fuzzy
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr "Aktion \"Rechtsklick\" zuweisen"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
+"Sie müssen der Aktion \"Rechtsklick\" eine Taste zuweisen, um dieses Spiel "
+"spielen zu können."
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr "Aktion \"Werkzeugleiste verbergen\" zuweisen"
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+"Sie müssen der Aktion \"Werkzeugleiste verbergen\" eine Taste zuweisen, um "
+"dieses Spiel spielen zu können."
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr "Aktion \"Herauszoomen\" zuweisen (optional)"
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr "Aktion \"Hineinzoomen\" zuweisen (optional)"
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
+"Vergessen Sie nicht, der Aktion \"Werkzeugleiste verbergen\" eine Taste "
+"zuzuweisen, um das ganze Inventar sehen zu können."
+
+#~ msgctxt "context"
+#~ msgid "Game Path:"
+#~ msgstr "Spielpfad:"
+
+#~ msgctxt "lowres"
+#~ msgid "Preferred Device:"
+#~ msgstr "Standard-Gerät:"
+
+#~ msgctxt "lowres"
+#~ msgid "True Roland MT-32 (disable GM emulation)"
+#~ msgstr "Echte Roland-MT-32-Emulation (kein GM)"
+
+#~ msgid "Save Path: "
+#~ msgstr "Spielstände: "
+
+#~ msgctxt "lowres"
+#~ msgid "Save Path: "
+#~ msgstr "Speichern: "
+
+#~ msgctxt "lowres"
+#~ msgid "Standard Renderer (16bpp)"
+#~ msgstr "Standard-Renderer(16bpp)"
+
+#~ msgctxt "lowres"
+#~ msgid "Antialiased Renderer (16bpp)"
+#~ msgstr "Kantenglättung (16bpp)"
+
#~ msgctxt "lowres"
#~ msgid "Special sound effects volume"
#~ msgstr "Lautstärke spezieller Soundeffekte"
diff --git a/po/es_ES.po b/po/es_ES.po
index f8e2d0f87c..dbc9cc9b5c 100644
--- a/po/es_ES.po
+++ b/po/es_ES.po
@@ -1,20 +1,20 @@
-# LANGUAGE translation for ScummVM.
-# Copyright (C) YEAR ScummVM Team
+# Spanish translation for ScummVM.
+# Copyright (C) 2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Tomás Maidagan, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-07-30 22:17+0100\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-09-25 17:11+0100\n"
"Last-Translator: Tomás Maidagan\n"
"Language-Team: \n"
+"Language: Espanol\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Espanol\n"
#: gui/about.cpp:96
#, c-format
@@ -29,51 +29,60 @@ msgstr "Características compiladas:"
msgid "Available engines:"
msgstr "Motores disponibles:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "Arriba"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "Ir al directorio anterior"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "Arriba"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "Cancelar"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
-msgstr "Elegir"
+msgstr "Aceptar"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "Cerrar"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "Clic de ratón"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "Mostrar el teclado"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "Asignar teclas"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "Elige la acción a asociar"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "Asignar"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
-msgstr "De acuerdo"
+msgstr "Aceptar"
#: gui/KeysDialog.cpp:52
msgid "Select an action and click 'Map'"
@@ -97,19 +106,15 @@ msgstr "Por favor, selecciona una acción"
msgid "Press the key to associate"
msgstr "Pulsa la tecla a asignar"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "Elige la acción a asociar"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "Juego"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "ID:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -117,19 +122,29 @@ msgstr ""
"Identificador usado para las partidas guardadas y para ejecutar el juego "
"desde la línea de comando"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "Nombre:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "Título completo del juego"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "Nom.:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Idioma:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -137,277 +152,282 @@ msgstr ""
"Idioma del juego. No sirve para pasar al inglés la versión española de un "
"juego"
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<por defecto>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "Plataforma:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "Plataforma para la que se diseñó el juego"
-#: gui/launcher.cpp:197
-#, fuzzy
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
-msgstr "Plataforma:"
+msgstr "Plat.:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Gráficos"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "GFX"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "Ignorar opciones gráficas generales"
-#: gui/launcher.cpp:214
-#, fuzzy
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
-msgstr "Ignorar opciones gráficas generales"
+msgstr "Opciones gráficas específicas"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Sonido"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "Ignorar opciones de sonido generales"
-#: gui/launcher.cpp:226
-#, fuzzy
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
-msgstr "Ignorar opciones de sonido generales"
+msgstr "Opciones de sonido específicas"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Volumen"
-#: gui/launcher.cpp:237 gui/options.cpp:951
-#, fuzzy
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "Volumen"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "Ignorar opciones de volumen generales"
-#: gui/launcher.cpp:242
-#, fuzzy
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
-msgstr "Ignorar opciones de volumen generales"
+msgstr "Opciones de volumen específicas"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
-msgstr "Ignorar opciones MIDI generales"
+msgstr "Ignorar opciones de MIDI generales"
-#: gui/launcher.cpp:254
-#, fuzzy
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
-msgstr "Ignorar opciones MIDI generales"
+msgstr "Opciones de MIDI específicas"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr "MT-32"
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
-msgstr "Ignorar opciones MT-32 generales"
+msgstr "Ignorar opciones de MT-32 generales"
-#: gui/launcher.cpp:269
-#, fuzzy
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
-msgstr "Ignorar opciones MT-32 generales"
+msgstr "Opciones de MT-32 específicas"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "Rutas"
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr "Rutas"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
msgid "Game Path:"
msgstr "Juego:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:297
+msgctxt "lowres"
+msgid "Game Path:"
+msgstr "Juego:"
+
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "Adicional:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "Especifica un directorio para datos adicionales del juego"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Adicional:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "Partidas:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "Especifica dónde guardar tus partidas"
-#: gui/launcher.cpp:296
-#, fuzzy
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "Partidas:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
-#, fuzzy
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
-msgstr "Ninguno"
+msgstr "Ninguna"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "Por defecto"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
-msgstr "Seleccionar SoundFont"
+msgstr "Selecciona un SoundFont"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
-msgstr "Seleccionar directorio con los archivos del juego"
+msgstr "Selecciona el directorio del juego"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
-msgstr "Seleccionar directorio de juego adicional"
+msgstr "Selecciona el directorio adicional"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
-msgstr "Seleccionar directorio para partidas guardadas"
+msgstr "Selecciona el directorio para partidas guardadas"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "Esta ID ya está siendo usada. Por favor, elige otra."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~S~alir"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "Cerrar ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "Acerca ~d~e"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "Acerca de ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
-msgstr "~O~opciones..."
+msgstr "~O~pciones..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "Cambiar opciones generales de ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "~J~ugar"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "Jugar al juego seleccionado"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~C~argar..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "Cargar partida del juego seleccionado"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~A~ñadir juego..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
-msgstr "Mantén pulsado Mayús para añadir varios"
+msgstr "Mantener pulsado Mayús para añadir varios juegos"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "~E~ditar juego..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "Cambiar opciones de juego"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "E~l~iminar juego"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
-msgstr "Elimina el juego de la lista. Los archivos no se borran"
+msgstr "Eliminar el juego de la lista. Los archivos no se borran"
-#: gui/launcher.cpp:563
-#, fuzzy
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
-msgstr "~A~ñadir juego..."
+msgstr "~A~ñadir..."
-#: gui/launcher.cpp:565
-#, fuzzy
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
-msgstr "~E~ditar juego..."
+msgstr "~E~ditar..."
-#: gui/launcher.cpp:567
-#, fuzzy
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
-msgstr "E~l~iminar juego"
+msgstr "E~l~iminar"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "Buscar en la lista de juegos"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "Buscar:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "Eliminar valor"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "Cargar juego:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "Cargar"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -415,64 +435,62 @@ msgstr ""
"¿Seguro que quieres ejecutar la detección masiva? Puede que se añada un gran "
"número de juegos."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "Sí"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "No"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "¡ScummVM no ha podido abrir el directorio!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "¡ScummVM no ha encontrado ningún juego en el directorio!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "Elige el juego:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "¿Seguro que quieres eliminar la configuración de este juego?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr "Este juego no permite cargar partidas desde el lanzador."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr ""
"¡ScummVM no ha podido encontrar ningún motor capaz de ejecutar el juego!"
-#: gui/launcher.cpp:1044
-#, fuzzy
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
-msgstr "Añadir varios..."
+msgstr "Añad. varios"
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "Añadir varios..."
-#: gui/launcher.cpp:1045
-#, fuzzy
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
-msgstr "Añadir juego..."
+msgstr "Añadir..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "Añadir juego..."
@@ -540,8 +558,7 @@ msgid "48 kHz"
msgstr "48 kHz"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
-#, fuzzy
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "Ninguno"
@@ -552,7 +569,7 @@ msgstr "Modo gráfico:"
#: gui/options.cpp:646
msgid "Render mode:"
-msgstr "Modo de renderizado:"
+msgstr "Renderizado:"
#: gui/options.cpp:646 gui/options.cpp:647
msgid "Special dithering modes supported by some games"
@@ -570,38 +587,48 @@ msgstr "Corrección de aspecto"
msgid "Correct aspect ratio for 320x200 games"
msgstr "Corregir relación de aspecto en juegos 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
-msgstr "Dispositivo preferido:"
+msgstr "Disp. preferido:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Music Device:"
-msgstr "Dispositivo de música:"
+msgstr "Disp. de música:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
"Especifica qué dispositivo de sonido o emulador de tarjeta de sonido "
"prefieres"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr ""
"Especifica el dispositivo de sonido o emulador de tarjeta de sonido de salida"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "Disp. preferido:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Disp. de música:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
-msgstr "Emulador de AdLib:"
+msgstr "Emul. de AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "AdLib se usa para la música en muchos juegos"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
-msgstr "Frecuencia de salida:"
+msgstr "Frec. de salida:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -609,55 +636,56 @@ msgstr ""
"Los valores más altos ofrecen mayor calidad, pero puede que tu tarjeta de "
"sonido no sea compatible"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "Dispositivo GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr "Especifica el dispositivo de salida General MIDI por defecto"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
-msgstr "Algunas tarjetas de sonido, Fluidsynth y Timidity soportan SoundFont"
+msgstr ""
+"SoundFont está soportado por algunas tarjetas de sonido, además de "
+"Fluidsynth y Timidity"
-#: gui/options.cpp:738
-#, fuzzy
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "Modo AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "Usar tanto MIDI como AdLib en la generación de sonido"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "Ganancia MIDI:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "MT-32 Device:"
-msgstr "Dispositivo MT-32:"
+msgstr "Disp. MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"Especifica el dispositivo de sonido para la salida Roland MT-32/LAPC1/CM32l/"
"CM64 por defecto"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Roland MT-32 auténtica (desactivar emulación GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -665,188 +693,191 @@ msgstr ""
"Marcar si se quiere usar un dispositivo de sonido real conectado al "
"ordenador y compatible con Roland"
-#: gui/options.cpp:763
-#, fuzzy
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
-msgstr "Roland MT-32 auténtica (desactivar emulación GM)"
+msgid "True Roland MT-32 (no GM emulation)"
+msgstr "Roland MT-32 real (sin emulación GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Activar modo Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr "Desactiva la conversión General MIDI en juegos con sonido Roland MT-32"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Texto y voces:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr "Voces"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "Subtítulos"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "Ambos"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
-msgstr "Velocidad de los subtítulos:"
+msgstr "Vel. de subtítulos:"
-#: gui/options.cpp:801
-#, fuzzy
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Texto y voces:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
-msgstr "Voces"
+msgstr "Voz"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
-msgstr "Subt."
+msgstr "Subt"
-#: gui/options.cpp:807
-#, fuzzy
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
-msgstr "Ambos"
+msgstr "V&S"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "Reproducir voces y subtítulos"
-#: gui/options.cpp:809
-#, fuzzy
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
-msgstr "Velocidad de los subtítulos:"
+msgstr "Vel. de subt.:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
-msgstr "Volumen de la música:"
+msgstr "Música:"
-#: gui/options.cpp:827
-#, fuzzy
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
-msgstr "Volumen de la música:"
+msgstr "Música:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Silenciar"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
-msgstr "Volumen de los efectos"
+msgstr "Efectos:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "Volumen de los efectos de sonido"
-#: gui/options.cpp:839
-#, fuzzy
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
-msgstr "Volumen de los efectos"
+msgstr "Efectos:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
-msgstr "Volumen de las voces"
+msgstr "Voces:"
-#: gui/options.cpp:849
-#, fuzzy
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
-msgstr "Volumen de las voces"
+msgstr "Voces:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "Partidas:"
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "Temas:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr "Temas:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr "Especifica el directorio adicional usado por los juegos y ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
msgid "Plugins Path:"
msgstr "Plugins:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1006
+msgctxt "lowres"
+msgid "Plugins Path:"
+msgstr "Plugins:"
+
+#: gui/options.cpp:1015
msgid "Misc"
-msgstr "Otros"
+msgstr "Otras"
-#: gui/options.cpp:999
-#, fuzzy
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
-msgstr "Otros"
+msgstr "Otras"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Tema:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
-msgstr "Render de la interfaz"
+msgstr "Interfaz:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Autoguardado:"
-#: gui/options.cpp:1019
-#, fuzzy
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr "Autoguardado:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Teclas"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
-msgstr "Idioma de la interfaz:"
+msgstr "Idioma:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Idioma de la interfaz de ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr "Tienes que reiniciar ScummVM para aplicar los cambios."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
-msgstr "Selecciona el directorio para partidas guardadas."
+msgstr "Selecciona el directorio de guardado"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
"No se puede escribir en el directorio elegido. Por favor, selecciona otro."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
-msgstr "Selecciona el directorio para temas de interfaz"
+msgstr "Selecciona el directorio de temas"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
-msgstr "Selecciona el directorio para archivos adicionales"
+msgstr "Selecciona el directorio adicional"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
-msgstr "Selecciona el directorio para plugins"
+msgstr "Selecciona el directorio de plugins"
+
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
@@ -858,7 +889,7 @@ msgstr "No hay hora guardada"
#: gui/saveload.cpp:62 gui/saveload.cpp:243
msgid "No playtime saved"
-msgstr "No hay tiempo de juego guardado"
+msgstr "No hay tiempo guardado"
#: gui/saveload.cpp:69 gui/saveload.cpp:157
msgid "Delete"
@@ -870,15 +901,15 @@ msgstr "¿Seguro que quieres borrar esta partida?"
#: gui/saveload.cpp:265
msgid "Date: "
-msgstr "Fecha:"
+msgstr "Fecha: "
#: gui/saveload.cpp:268
msgid "Time: "
-msgstr "Hora:"
+msgstr "Hora: "
#: gui/saveload.cpp:273
msgid "Playtime: "
-msgstr "Tiempo de juego:"
+msgstr "Tiempo: "
#: gui/saveload.cpp:286 gui/saveload.cpp:353
msgid "Untitled savestate"
@@ -888,29 +919,30 @@ msgstr "Partida sin nombre"
msgid "Select a Theme"
msgstr "Selecciona un tema"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "GFX desactivados"
-#: gui/ThemeEngine.cpp:335
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "GFX desactivados"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "Estándar (16bpp)"
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
-msgstr "Antialiasing (16bpp)"
-
-#: gui/ThemeEngine.cpp:341
-#, fuzzy
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
msgstr "Estándar (16bpp)"
-#: gui/ThemeEngine.cpp:342
-#, fuzzy
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
-msgstr "Antialiasing (16bpp)"
+msgstr "Suavizado (16bpp)"
+
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr "Suavizado (16bpp)"
#: base/main.cpp:205
#, c-format
@@ -936,11 +968,11 @@ msgstr "Pausar"
msgid "Skip line"
msgstr "Saltar frase"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "Error al ejecutar el juego:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr "No se ha podido encontrar ningún motor capaz de ejecutar el juego"
@@ -986,11 +1018,11 @@ msgstr "Imposible crear el archivo"
#: common/error.cpp:57
msgid "Reading failed"
-msgstr "Lectura fallida"
+msgstr "Fallo de lectura"
#: common/error.cpp:58
msgid "Writing data failed"
-msgstr "Escritura de datos fallida"
+msgstr "Fallo en la escritura de datos"
#: common/error.cpp:60 common/error.cpp:71
msgid "Unknown Error"
@@ -1005,13 +1037,11 @@ msgid "Hercules Amber"
msgstr "Hercules ámbar"
#: common/util.cpp:262
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Green"
msgstr "Hercules verde"
#: common/util.cpp:263
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Amber"
msgstr "Hercules ámbar"
@@ -1030,7 +1060,7 @@ msgstr "~G~uardar"
#: engines/dialogs.cpp:99
msgid "~O~ptions"
-msgstr "~O~opciones"
+msgstr "~O~pciones"
#: engines/dialogs.cpp:104
msgid "~H~elp"
@@ -1045,16 +1075,18 @@ msgid "~R~eturn to Launcher"
msgstr "~V~olver al lanzador"
#: engines/dialogs.cpp:112
-#, fuzzy
msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "~V~olver al lanzador"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "Guardar partida"
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1089,6 +1121,39 @@ msgstr "Si~g~uiente"
msgid "~C~lose"
msgstr "Cerra~r~"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"Fallo al guardar en el archivo:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"Fallo al cargar desde el archivo:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"Partida guardada en el archivo:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "Modo ~Z~ip activado"
@@ -1101,22 +1166,29 @@ msgstr "Tra~n~siciones activadas"
msgid "~W~ater Effect Enabled"
msgstr "Efecto ag~u~a activado"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "Cargar partida:"
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "Cargar"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
-msgstr "Emulador de MAME OPL"
+msgstr "Emulador OPL de MAME"
#: sound/fmopl.cpp:53
msgid "DOSBox OPL emulator"
-msgstr "Emulador de DOSBox OPL"
+msgstr "Emulador OPL de DOSBox"
#: sound/null.h:45
msgid "No music"
msgstr "Sin música"
#: sound/mods/paula.cpp:192
-#, fuzzy
msgid "Amiga Audio Emulator"
-msgstr "Emulador de AdLib"
+msgstr "Emulador de Amiga Audio"
#: sound/softsynth/adlib.cpp:1590
msgid "AdLib Emulator"
@@ -1124,12 +1196,11 @@ msgstr "Emulador de AdLib"
#: sound/softsynth/appleiigs.cpp:36
msgid "Apple II GS Emulator (NOT IMPLEMENTED)"
-msgstr ""
+msgstr "Emulador de Apple II GS (NO IMPLEMENTADO)"
#: sound/softsynth/sid.cpp:1434
-#, fuzzy
msgid "C64 Audio Emulator"
-msgstr "Emulador de AdLib"
+msgstr "Emulador de C64 Audio"
#: sound/softsynth/mt32.cpp:327
msgid "Initialising MT-32 Emulator"
@@ -1243,11 +1314,11 @@ msgstr "Sonido de alta calidad (más lento) (reinicio)"
msgid "Disable power off"
msgstr "Desactivar apagado"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "Modo Touchpad activado."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "Modo Touchpad desactivado."
@@ -1255,13 +1326,12 @@ msgstr "Modo Touchpad desactivado."
#: backends/platform/wince/wince-sdl.cpp:111
#: backends/platform/wince/wince-sdl.cpp:118
msgid "Normal (no scaling)"
-msgstr "Normal (sin escalado)"
+msgstr "Normal (sin reescalado)"
#: backends/platform/sdl/graphics.cpp:59
-#, fuzzy
msgctxt "lowres"
msgid "Normal (no scaling)"
-msgstr "Normal (sin escalado)"
+msgstr "Normal"
#: backends/platform/symbian/src/SymbianActions.cpp:41
#: backends/platform/wince/CEActionsSmartphone.cpp:38
@@ -1339,7 +1409,7 @@ msgstr "Teclado virtual"
msgid "Key mapper"
msgstr "Asignación de teclas"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "¿Quieres salir?"
@@ -1469,7 +1539,7 @@ msgstr "Se ha excedido el tiempo de inicialización de red"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "Red no inicializada (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1555,10 +1625,32 @@ msgstr "Pantalla"
msgid "Do you want to perform an automatic scan ?"
msgstr "¿Quieres realizar una búsqueda automática?"
+#: backends/platform/wince/wince-sdl.cpp:990
#, fuzzy
-#~ msgctxt "lowres"
-#~ msgid "Special sound effects volume"
-#~ msgstr "Volumen de los efectos de sonido"
+msgid "Map right click action"
+msgstr "Clic derecho"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
-#~ msgid "English"
-#~ msgstr "Inglés"
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
diff --git a/po/fr_FR.po b/po/fr_FR.po
index 8d0af1d8b7..8c90123860 100644
--- a/po/fr_FR.po
+++ b/po/fr_FR.po
@@ -1,20 +1,20 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR ScummVM Team
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# French translation for ScummVM.
+# Copyright (C) 2010 ScummVM Team
+# This file is distributed under the same license as the ScummVM package.
+# Thierry Crozat <criezy@scummvm.org>, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-08-11 22:14+0100\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-10-01 22:12+0100\n"
"Last-Translator: Thierry Crozat <criezy@scummvm.org>\n"
"Language-Team: French <scummvm-devel@lists.sf.net>\n"
+"Language: Francais\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Francais\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: gui/about.cpp:96
@@ -30,47 +30,56 @@ msgstr "Options incluses:"
msgid "Available engines:"
msgstr "Moteurs disponibles:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "Remonter"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "Remonte d'un niveau dans la hiérarchie de répertoire"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "Remonter"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "Annuler"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "Choisir"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "Fermer"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "Clic de souris"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "Afficher le clavier"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "Changer l'affectation des touches"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "Sélectionnez une action à affecter"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "Affecter"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -98,39 +107,45 @@ msgstr "Selectionnez une action"
msgid "Press the key to associate"
msgstr "Appuyez sur la touche à associer"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "Sélectionnez une action à affecter"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "Jeu"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "ID:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
msgstr ""
-"ID compact du jeu utilisé pour identifier les sauvegardes et démarrer le jeu "
-"depuis la ligne de commande"
+"ID compact du jeu utilisée pour identifier les sauvegardes et démarrer le "
+"jeu depuis la ligne de commande"
+
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "Nom:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "Nom complet du jeu"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "Nom:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Langue:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -138,267 +153,283 @@ msgstr ""
"Langue du jeu. Cela ne traduira pas en anglais par magie votre version "
"espagnole du jeu."
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<defaut>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "Plateforme:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "Plateforme pour laquelle votre jeu a été conçu"
-#: gui/launcher.cpp:197
-#, fuzzy
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr "Système:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Graphique"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "GFX"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "Utiliser des réglages graphiques spécifiques à ce jeux"
-#: gui/launcher.cpp:214
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr "Réglages spécifiques à ce jeux"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Audio"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "Utiliser des réglages audio spécifiques à ce jeux"
-#: gui/launcher.cpp:226
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr "Réglages spécifiques à ce jeux"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Volume"
-#: gui/launcher.cpp:237 gui/options.cpp:951
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "Volume"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "Utiliser des réglages de volume sonore spécifiques à ce jeux"
-#: gui/launcher.cpp:242
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr "Réglages spécifiques à ce jeux"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "Utiliser des réglages MIDI spécifiques à ce jeux"
-#: gui/launcher.cpp:254
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr "Réglages spécifiques à ce jeux"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr "MT-32"
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr "Utiliser des réglages MT-32 spécifiques à ce jeux"
-#: gui/launcher.cpp:269
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr "Réglages spécifiques à ce jeux"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "Chemins"
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr "Chemins"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
+msgid "Game Path:"
+msgstr "Chemin du Jeu:"
+
+#: gui/launcher.cpp:297
+msgctxt "lowres"
msgid "Game Path:"
msgstr "Chemin du Jeu:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "Extra:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "Définie un chemin vers des données suplémentaires utilisées par le jeu"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Extra:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "Sauvegardes:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "Définie l'emplacement où les fichiers de sauvegarde sont créés"
-#: gui/launcher.cpp:296
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "Sauvegardes:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "Aucun"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "Défaut"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "Choisir une banque de sons"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "Sélectionner le répertoire contenant les données du jeu"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "Sélectionner un répertoire supplémentaire"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "Sélectionner le répertoire pour les sauvegardes"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "Cet ID est déjà utilisé par un autre jeu. Choisissez en un autre svp."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~Q~uitter"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "Quitter ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "À ~P~ropos..."
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "À propos de ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~O~ptions..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "Change les options globales de ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "~D~émarrer"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "Démarre le jeu sélectionné"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~C~harger"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "Charge une sauvegarde pour le jeu sélectionné"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~A~jouter..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr ""
"Ajoute un jeu à la Liste. Maintenez Shift enfoncée pour un Ajout Massif"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "~E~diter..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "Change les options du jeu"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "~S~upprimer"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr "Supprime le jeu de la liste. Les fichiers sont conservés"
-#: gui/launcher.cpp:563
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr "~A~jouter..."
-#: gui/launcher.cpp:565
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr "~E~diter..."
-#: gui/launcher.cpp:567
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr "~S~upprimer"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "Recherche dans la liste de jeux"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "Filtre:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "Effacer la valeur"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "Charger le jeu:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "Charger"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -406,64 +437,62 @@ msgstr ""
"Voulez-vous vraiment lancer la détection automatique des jeux? Cela peut "
"potentiellement ajouter un grand nombre de jeux."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "Oui"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "Non"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "ScummVM n'a pas pu ouvrir le répertoire sélectionné."
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "ScummVM n'a pas trouvé de jeux dans le répertoire sélectionné."
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "Choisissez le jeu:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "Voulez-vous vraiment supprimer ce jeu?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr ""
"Le chargement de sauvegarde depuis le lanceur n'est pas supporté pour ce jeu."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr "ScummVM n'a pas pu trouvé de moteur pour lancer le jeu sélectionné."
-#: gui/launcher.cpp:1044
-#, fuzzy
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr "Ajout Massif..."
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "Ajout Massif..."
-#: gui/launcher.cpp:1045
-#, fuzzy
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr "Ajouter..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "Ajouter..."
@@ -531,7 +560,7 @@ msgid "48 kHz"
msgstr "48 kHz"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "Aucune"
@@ -560,37 +589,47 @@ msgstr "Correction du rapport d'aspect"
msgid "Correct aspect ratio for 320x200 games"
msgstr "Corrige le rapport d'aspect pour les jeu 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
msgstr "Sortie Préféré:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Music Device:"
msgstr "Sortie Audio:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
"Spécifie le périphérique de sortie audio ou l'émulateur de carte audio "
"préféré"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr "Spécifie le périphérique de sortie audio ou l'émulateur de carte audio"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "Sortie Préféré:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Sortie Audio:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "Émulateur AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "AdLib est utilisé pour la musique dans de nombreux jeux"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "Fréquence:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -598,56 +637,56 @@ msgstr ""
"Une valeur plus élevée donne une meilleure qualité audio mais peut ne pas "
"être supporté par votre carte son"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "Sortie GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr "Spécifie le périphérique audio par défaut pour la sortie General MIDI"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "Banque de sons:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
"La banque de sons (SoundFont) est utilisée par certaines cartes audio, "
"Fluidsynth et Timidity"
-#: gui/options.cpp:738
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "Mode mixe AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "Utiliser à la fois MIDI et AdLib"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "Gain MIDI:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "MT-32 Device:"
msgstr "Sortie MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"Spécifie le périphérique audio par défaut pour la sortie Roland MT-32/LAPC1/"
"CM32l/CM64"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Roland MT-32 exacte (désactive l'émulation GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -655,197 +694,209 @@ msgstr ""
"Vérifie si vous voulez utiliser un périphérique audio compatible Roland "
"connecté à l'ordinateur"
-#: gui/options.cpp:763
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr "Roland MT-32 exacte (pas d'ému GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Activer le mode Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr "Désactiver la conversion des pistes MT-32 en General MIDI"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Dialogue:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
-msgstr "Audio"
+msgstr "Voix"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "Sous-titres"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "Les deux"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr "Vitesse des ST:"
-#: gui/options.cpp:801
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Dialogue:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
-msgstr "Audio"
+msgstr "Voix"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "Subs"
-#: gui/options.cpp:807
-#, fuzzy
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
-msgstr "Les deux"
+msgstr "V&S"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "Affiche les sous-titres et joue les dialogues audio"
-#: gui/options.cpp:809
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "Vitesse des ST:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "Volume Musique:"
-#: gui/options.cpp:827
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr "Musique:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Silence"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "Volume Bruitage:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "Volume des effets spéciaux sonores"
-#: gui/options.cpp:839
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr "Bruitage:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr "Volume Dialogues:"
-#: gui/options.cpp:849
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
msgstr "Dialogues:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "Sauvegardes:"
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "Thèmes:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr "Thèmes:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
"Spécifie un chemin vers des données supplémentaires utilisées par tous les "
"jeux ou ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
+msgid "Plugins Path:"
+msgstr "Plugins:"
+
+#: gui/options.cpp:1006
+msgctxt "lowres"
msgid "Plugins Path:"
msgstr "Plugins:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "Divers"
-#: gui/options.cpp:999
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "Divers"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Thème:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr "Interface:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Sauvegarde auto:"
-#: gui/options.cpp:1019
-#, fuzzy
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr "Sauvegarde:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Touches"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr "Langue:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Langue de l'interface graphique de ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr ""
"Vous devez relancer ScummVM pour que le changement soit pris en compte."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "Sélectionner le répertoire pour les sauvegardes"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
"Le répertoire sélectionné est vérouillé en écriture. Sélectionnez un autre "
"répertoire."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr "Sélectionner le répertoire des thèmes d'interface"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "Sélectionner le répertoire pour les fichiers suplémentaires"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "Sélectionner le répertoire des plugins"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+"Le thème que vous avez sélectioné ne support pas la langue française. Si "
+"vous voulez l'utiliser vous devez d'abord changer de langue."
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
-msgstr "Date non sauvée"
+msgstr "Date inconnue"
#: gui/saveload.cpp:61 gui/saveload.cpp:242
msgid "No time saved"
-msgstr "Heure non sauvée"
+msgstr "Heure inconnue"
#: gui/saveload.cpp:62 gui/saveload.cpp:243
msgid "No playtime saved"
-msgstr "Durée de jeu non sauvée"
+msgstr "Durée de jeu inconnue"
#: gui/saveload.cpp:69 gui/saveload.cpp:157
msgid "Delete"
@@ -857,15 +908,15 @@ msgstr "Voulez-vous vraiment supprimer cette sauvegarde?"
#: gui/saveload.cpp:265
msgid "Date: "
-msgstr "Date:"
+msgstr "Date: "
#: gui/saveload.cpp:268
msgid "Time: "
-msgstr "Heure:"
+msgstr "Heure: "
#: gui/saveload.cpp:273
msgid "Playtime: "
-msgstr "Durée de jeu:"
+msgstr "Durée de jeu: "
#: gui/saveload.cpp:286 gui/saveload.cpp:353
msgid "Untitled savestate"
@@ -875,26 +926,29 @@ msgstr "Sauvegarde sans nom"
msgid "Select a Theme"
msgstr "Sélectionnez un Thème"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "GFX désactivé"
-#: gui/ThemeEngine.cpp:335
-msgid "Standard Renderer (16bpp)"
-msgstr "Standard (16bpp)"
-
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
-msgstr "Anti-crénelé (16 bpp)"
-
-#: gui/ThemeEngine.cpp:341
+#: gui/ThemeEngine.cpp:332
msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "GFX désactivé"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
+msgstr "Rendu Standard (16bpp)"
+
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
msgstr "Standard (16bpp)"
-#: gui/ThemeEngine.cpp:342
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
+msgstr "Rendu Anti-crénelé (16 bpp)"
+
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
msgstr "Anti-crénelé (16 bpp)"
#: base/main.cpp:205
@@ -921,11 +975,11 @@ msgstr "Mettre en pause"
msgid "Skip line"
msgstr "Passer la phrase"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "Erreur lors de l'éxécution du jeu:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr "Impossible de trouver un moteur pour exécuter le jeu sélectionné"
@@ -1032,11 +1086,14 @@ msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "Retour au ~L~anceur"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "Sauvegarde:"
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1071,6 +1128,39 @@ msgstr "~S~uivant"
msgid "~C~lose"
msgstr "~F~ermer"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"Échec de l'enregistrement de l'état du jeu dans le fichier:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"Échec du chargement de l'état du jeu depuis le fichier:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"État du jeu enregistré avec succès dans le fichier:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "Mode ~Z~ip Activé"
@@ -1083,6 +1173,14 @@ msgstr "T~r~ansitions activé"
msgid "~W~ater Effect Enabled"
msgstr "~E~ffets de l'Eau Activés"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "Charger le jeu:"
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "Charger"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "Émulateur MAME OPL"
@@ -1223,11 +1321,11 @@ msgstr "Audio haute qualité (plus lent) (redémarrer)"
msgid "Disable power off"
msgstr "Désactivé l'extinction"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "Mode touchpad activé"
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "Mode touchpad désactivé"
@@ -1315,11 +1413,10 @@ msgid "Virtual keyboard"
msgstr "Clavier virtuel"
#: backends/platform/symbian/src/SymbianActions.cpp:59
-#, fuzzy
msgid "Key mapper"
msgstr "Affectation des touches"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "Voulez-vous quitter?"
@@ -1332,7 +1429,6 @@ msgid "Current video mode:"
msgstr "Mode vidéo actuel"
#: backends/platform/wii/options.cpp:56
-#, fuzzy
msgid "Double-strike"
msgstr "Coup double"
@@ -1385,7 +1481,6 @@ msgid "Server:"
msgstr "Serveur:"
#: backends/platform/wii/options.cpp:110
-#, fuzzy
msgid "Share:"
msgstr "Disque partagé:"
@@ -1422,27 +1517,22 @@ msgid "DVD not mounted"
msgstr "DVD non monté"
#: backends/platform/wii/options.cpp:161
-#, fuzzy
msgid "Network up, share mounted"
msgstr "Réseau connecté, disque partagé monté"
#: backends/platform/wii/options.cpp:163
-#, fuzzy
msgid "Network up"
msgstr "Réseau connecté"
#: backends/platform/wii/options.cpp:166
-#, fuzzy
msgid ", error while mounting the share"
msgstr ", échec du montage du disque partagé"
#: backends/platform/wii/options.cpp:168
-#, fuzzy
msgid ", share not mounted"
msgstr ", disque partagé non monté"
#: backends/platform/wii/options.cpp:174
-#, fuzzy
msgid "Network down"
msgstr "Réseau déconnecté"
@@ -1456,7 +1546,7 @@ msgstr "Dépassement du délai lors de l'initialisation du réseau"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "Réseau non initialisé (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1480,50 +1570,42 @@ msgid "Show/Hide Cursor"
msgstr "Afficher/Cacher le curseur"
#: backends/platform/wince/CEActionsPocket.cpp:50
-#, fuzzy
msgid "Free look"
msgstr "Regarder autour"
#: backends/platform/wince/CEActionsPocket.cpp:51
-#, fuzzy
msgid "Zoom up"
msgstr "Dézoomer"
#: backends/platform/wince/CEActionsPocket.cpp:52
-#, fuzzy
msgid "Zoom down"
msgstr "Zoomer"
#: backends/platform/wince/CEActionsPocket.cpp:54
#: backends/platform/wince/CEActionsSmartphone.cpp:48
-#, fuzzy
msgid "Bind Keys"
msgstr "Affecter les touches"
#: backends/platform/wince/CEActionsPocket.cpp:55
-#, fuzzy
msgid "Cursor Up"
msgstr "Haut"
#: backends/platform/wince/CEActionsPocket.cpp:56
-#, fuzzy
msgid "Cursor Down"
msgstr "Bas"
#: backends/platform/wince/CEActionsPocket.cpp:57
-#, fuzzy
msgid "Cursor Left"
msgstr "Gauche"
#: backends/platform/wince/CEActionsPocket.cpp:58
-#, fuzzy
msgid "Cursor Right"
msgstr "Droit"
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Do you want to load or save the game?"
-msgstr "Voulez-vous charger ou sauver le jeu?"
+msgstr "Voulez-vous charger ou enregistrer le jeu?"
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
@@ -1550,12 +1632,40 @@ msgstr "Affichage"
msgid "Do you want to perform an automatic scan ?"
msgstr "Voulez-vous exécuter une recherche automatique?"
-#~ msgctxt "lowres"
-#~ msgid "Special sound effects volume"
-#~ msgstr "Volume des effets spéciaux sonores"
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr "Affecter l'action 'Clic Droit'"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
+"Vous devez affecter une touche à l'action de 'Clic Droit' pour pouvoir jouer "
+"à ce jeu"
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr "Affecter l'action 'Cacher Bar d'Outils'"
-#~ msgid "English"
-#~ msgstr "Anglais"
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+"Vous devez affecter une touche à l'action 'Cacher Bar d'Outils' pour pouvoir "
+"jouer à ce jeu"
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr "Affecter l'action 'Dézoomer' (optionnelle)"
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr "Affecter l'action 'Zoomer' (optionnelle)"
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
+"Noubliez pas d'affecter une touche à l'action 'Cacher Bar d'Outils' pour "
+"pouvoir voir entièrement l'inventaire"
#~ msgid "Failed to load any GUI theme, aborting"
#~ msgstr "Aucun thème GUI n'a pu être chargé; abandon"
@@ -1581,57 +1691,15 @@ msgstr "Voulez-vous exécuter une recherche automatique?"
#~ msgid "%s failed to instantiate engine: %s (target '%s', path '%s')"
#~ msgstr ""
-#~ "Le plugin %s a échoué dans l'instanciation du moteur de jeu: %s (cible '%"
-#~ "s', chemin '%s')"
-
-#~ msgid "Ok"
-#~ msgstr "Ok"
+#~ "Le plugin %s a échoué dans l'instanciation du moteur de jeu: %s (cible "
+#~ "'%s', chemin '%s')"
#~ msgid "Music driver:"
#~ msgstr "Pilote audio:"
-#~ msgid "ALSA"
-#~ msgstr "ALSA"
-
#~ msgid "Atari ST MIDI"
#~ msgstr "MIDI Atari ST"
-#~ msgid "SEQ"
-#~ msgstr "SEQ"
-
-#~ msgid "DMedia"
-#~ msgstr "DMedia"
-
-#~ msgid "CAMD"
-#~ msgstr "CAMD"
-
-#~ msgid "CoreAudio"
-#~ msgstr "CoreAudio"
-
-#~ msgid "CoreMIDI"
-#~ msgstr "CoreMIDI"
-
-#~ msgid "Yamaha Pa1"
-#~ msgstr "Yamaha Pa1"
-
-#~ msgid "Tapwave Zodiac"
-#~ msgstr "Tapwave Zodiac"
-
-#~ msgid "FluidSynth"
-#~ msgstr "FluidSynth"
-
-#~ msgid "AdLib"
-#~ msgstr "AdLib"
-
-#~ msgid "IBM PCjr"
-#~ msgstr "IBM PCjr"
-
-#~ msgid "Creative Music System"
-#~ msgstr "Creative Music System"
-
-#~ msgid "TiMidity"
-#~ msgstr "TiMidity"
-
#~ msgid "About..."
#~ msgstr "A propos..."
diff --git a/po/hu_HU.po b/po/hu_HU.po
index 2ddaf1adae..0b1540cc70 100644
--- a/po/hu_HU.po
+++ b/po/hu_HU.po
@@ -1,20 +1,20 @@
-# LANGUAGE translation for ScummVM.
-# Copyright (C) 2009 ScummVM
+# Hungarian translation for ScummVM.
+# Copyright (C) 2009 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
# Alex Bevilacqua <alexbevi@gmail.com>, 2009.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM VERSION\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
"PO-Revision-Date: 2009-11-25 07:42-0500\n"
"Last-Translator: Alex Bevilacqua <alexbevi@gmail.com>\n"
"Language-Team: Hungarian\n"
+"Language: Magyar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=cp1250\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: gui/about.cpp:96
@@ -30,47 +30,56 @@ msgstr ""
msgid "Available engines:"
msgstr ""
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr ""
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr ""
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr ""
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr ""
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr ""
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr ""
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr ""
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr ""
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr ""
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr ""
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr ""
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -98,322 +107,348 @@ msgstr ""
msgid "Press the key to associate"
msgstr ""
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr ""
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr ""
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr ""
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
msgstr ""
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr ""
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr ""
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr ""
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr ""
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr ""
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
msgstr ""
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<alapértelmezett>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr ""
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr ""
-#: gui/launcher.cpp:197
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr ""
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Grafikával"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr ""
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr ""
-#: gui/launcher.cpp:214
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr ""
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Hang"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr ""
-#: gui/launcher.cpp:226
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr ""
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Volumene"
-#: gui/launcher.cpp:237 gui/options.cpp:951
+#: gui/launcher.cpp:243 gui/options.cpp:954
#, fuzzy
msgctxt "lowres"
msgid "Volume"
msgstr "Volumene"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr ""
-#: gui/launcher.cpp:242
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr ""
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr ""
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr ""
-#: gui/launcher.cpp:254
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr ""
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr ""
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr ""
-#: gui/launcher.cpp:269
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr ""
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "Ösvények"
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+#, fuzzy
+msgctxt "lowres"
msgid "Paths"
msgstr "Ösvények"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
+#, fuzzy
+msgid "Game Path:"
+msgstr "Extra Útvonal:"
+
+#: gui/launcher.cpp:297
#, fuzzy
+msgctxt "lowres"
msgid "Game Path:"
msgstr "Extra Útvonal:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "Extra Útvonal:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr ""
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+#, fuzzy
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Extra Útvonal:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
#, fuzzy
msgid "Save Path:"
msgstr "Extra Útvonal:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr ""
-#: gui/launcher.cpp:296
+#: gui/launcher.cpp:311 gui/options.cpp:987
#, fuzzy
msgctxt "lowres"
msgid "Save Path:"
msgstr "Extra Útvonal:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
#, fuzzy
msgctxt "path"
msgid "None"
msgstr "Semmi"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
#, fuzzy
msgid "Default"
msgstr "<alapértelmezett>"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr ""
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr ""
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr ""
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr ""
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr ""
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr ""
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr ""
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr ""
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr ""
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr ""
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr ""
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr ""
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr ""
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr ""
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr ""
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr ""
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr ""
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr ""
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr ""
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr ""
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr ""
-#: gui/launcher.cpp:563
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr ""
-#: gui/launcher.cpp:565
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr ""
-#: gui/launcher.cpp:567
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr ""
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr ""
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr ""
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr ""
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr ""
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr ""
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
msgstr ""
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr ""
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
@@ -421,45 +456,45 @@ msgstr ""
msgid "No"
msgstr "Semmi"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr ""
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr ""
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr ""
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr ""
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr ""
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr ""
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr ""
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr ""
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr ""
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr ""
@@ -527,7 +562,7 @@ msgid "48 kHz"
msgstr ""
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
+#: gui/options.cpp:555 gui/options.cpp:742
#, fuzzy
msgctxt "soundfont"
msgid "None"
@@ -557,275 +592,299 @@ msgstr "Aspect adag korrekció"
msgid "Correct aspect ratio for 320x200 games"
msgstr ""
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
msgstr ""
-#: gui/options.cpp:666
+#: gui/options.cpp:667
#, fuzzy
msgid "Music Device:"
msgstr "Zene mennyiség:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr ""
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr ""
+
+#: gui/options.cpp:669
+#, fuzzy
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Zene mennyiség:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "AdLib vezet :"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr ""
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "Kimeneti teljesítmény:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
msgstr ""
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr ""
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr ""
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr ""
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
-#: gui/options.cpp:738
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr ""
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "Vegyes AdLib/MIDI mód"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr ""
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "MIDI nyereség:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
#, fuzzy
msgid "MT-32 Device:"
msgstr "Zene mennyiség:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Igaz Roland MT-32 (megbénít GM emuláció)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
msgstr ""
-#: gui/options.cpp:763
+#: gui/options.cpp:766
#, fuzzy
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr "Igaz Roland MT-32 (megbénít GM emuláció)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Képessé Roland GS Mode"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Szöveg és beszéd:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
#, fuzzy
msgid "Speech"
msgstr "Csak a beszéd"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
#, fuzzy
msgid "Subtitles"
msgstr "Csak feliratok"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr ""
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr "Felirat sebesség:"
-#: gui/options.cpp:801
+#: gui/options.cpp:804
#, fuzzy
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Szöveg és beszéd:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr ""
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr ""
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr ""
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr ""
-#: gui/options.cpp:809
+#: gui/options.cpp:812
#, fuzzy
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "Felirat sebesség:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "Zene mennyiség:"
-#: gui/options.cpp:827
+#: gui/options.cpp:830
#, fuzzy
msgctxt "lowres"
msgid "Music volume:"
msgstr "Zene mennyiség:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Muta Összes"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "SFX mennyisége"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr ""
-#: gui/options.cpp:839
+#: gui/options.cpp:842
#, fuzzy
msgctxt "lowres"
msgid "SFX volume:"
msgstr "SFX mennyisége"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr "Beszéd mennyiség:"
-#: gui/options.cpp:849
+#: gui/options.cpp:852
#, fuzzy
msgctxt "lowres"
msgid "Speech volume:"
msgstr "Beszéd mennyiség:"
-#: gui/options.cpp:978
-msgid "Save Path: "
+#: gui/options.cpp:991
+msgid "Theme Path:"
msgstr ""
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+#, fuzzy
+msgctxt "lowres"
msgid "Theme Path:"
-msgstr ""
+msgstr "Extra Útvonal:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
+msgid "Plugins Path:"
+msgstr ""
+
+#: gui/options.cpp:1006
+msgctxt "lowres"
msgid "Plugins Path:"
msgstr ""
-#: gui/options.cpp:997
+#: gui/options.cpp:1015
msgid "Misc"
msgstr ""
-#: gui/options.cpp:999
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr ""
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Téma:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr "Leképez eszköz GUI:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Automatikus mentés:"
-#: gui/options.cpp:1019
+#: gui/options.cpp:1037
#, fuzzy
msgctxt "lowres"
msgid "Autosave:"
msgstr "Automatikus mentés:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Kulcsok"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr ""
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr ""
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr ""
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr ""
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr ""
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr ""
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr ""
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr ""
@@ -867,28 +926,31 @@ msgstr ""
msgid "Select a Theme"
msgstr ""
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr ""
-#: gui/ThemeEngine.cpp:335
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
msgstr ""
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard Renderer (16bpp)"
msgstr ""
-#: gui/ThemeEngine.cpp:341
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
msgstr ""
-#: gui/ThemeEngine.cpp:342
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr ""
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr ""
+
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
@@ -914,11 +976,11 @@ msgstr "Ösvények"
msgid "Skip line"
msgstr ""
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr ""
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr ""
@@ -1025,11 +1087,14 @@ msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr ""
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr ""
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1066,6 +1131,30 @@ msgstr ""
msgid "~C~lose"
msgstr ""
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr ""
@@ -1078,6 +1167,14 @@ msgstr ""
msgid "~W~ater Effect Enabled"
msgstr ""
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr ""
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr ""
+
#: sound/fmopl.cpp:51
#, fuzzy
msgid "MAME OPL emulator"
@@ -1223,11 +1320,11 @@ msgstr ""
msgid "Disable power off"
msgstr ""
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr ""
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr ""
@@ -1320,7 +1417,7 @@ msgstr ""
msgid "Key mapper"
msgstr ""
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr ""
@@ -1452,7 +1549,7 @@ msgstr ""
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr ""
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1540,6 +1637,50 @@ msgstr ""
msgid "Do you want to perform an automatic scan ?"
msgstr ""
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
+
+#, fuzzy
+#~ msgctxt "context"
+#~ msgid "Game Path:"
+#~ msgstr "Extra Útvonal:"
+
+#, fuzzy
+#~ msgctxt "lowres"
+#~ msgid "True Roland MT-32 (disable GM emulation)"
+#~ msgstr "Igaz Roland MT-32 (megbénít GM emuláció)"
+
+#, fuzzy
+#~ msgctxt "lowres"
+#~ msgid "Save Path: "
+#~ msgstr "Extra Útvonal:"
+
#~ msgid "Music driver:"
#~ msgstr "Zenei vezet :"
diff --git a/po/it_IT.po b/po/it_IT.po
index ba6ec80fd4..7a8f44d278 100644
--- a/po/it_IT.po
+++ b/po/it_IT.po
@@ -1,20 +1,20 @@
-# LANGUAGE translation for ScummVM.
-# Copyright (C) YEAR ScummVM Team
+# Italian translation for ScummVM.
+# Copyright (C) 2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Matteo 'Maff' Angelino <matteo.maff at gmail dot com>, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-06-30 23:56+0100\n"
-"Last-Translator: Maff <matteo.maff at gmail dot com>\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-09-21 19:33+0100\n"
+"Last-Translator: Matteo 'Maff' Angelino <matteo.maff at gmail dot com>\n"
"Language-Team: Italian\n"
+"Language: Italiano\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Italiano\n"
#: gui/about.cpp:96
#, c-format
@@ -29,47 +29,56 @@ msgstr "Funzionalità compilate in:"
msgid "Available engines:"
msgstr "Motori disponibili:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "Cartella superiore"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "Vai alla cartella superiore"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "Su"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "Annulla"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "Scegli"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "Chiudi"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "Clic del mouse"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "Mostra tastiera"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "Riprogramma tasti"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "Scegli un'azione da mappare"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "Mappa"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -97,19 +106,15 @@ msgstr "Seleziona un'azione"
msgid "Press the key to associate"
msgstr "Premi il tasto da associare"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "Scegli un'azione da mappare"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "Gioco"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "ID:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -117,297 +122,311 @@ msgstr ""
"Breve identificatore di gioco utilizzato per il riferimento a salvataggi e "
"per l'esecuzione del gioco dalla riga di comando"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "Nome:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "Titolo completo del gioco"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "Nome:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Lingua:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
msgstr ""
"Lingua del gioco. Un gioco inglese non potrà risultare tradotto in italiano"
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<predefinito>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "Piattaforma:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "La piattaforma per la quale il gioco è stato concepito"
-#: gui/launcher.cpp:197
-#, fuzzy
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
-msgstr "Piattaforma:"
+msgstr "Piattaf.:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "Grafica"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "Grafica"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "Ignora le impostazioni grafiche globali"
-#: gui/launcher.cpp:214
-#, fuzzy
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr "Ignora le impostazioni grafiche globali"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "Audio"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "Ignora le impostazioni audio globali"
-#: gui/launcher.cpp:226
-#, fuzzy
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr "Ignora le impostazioni audio globali"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "Volume"
-#: gui/launcher.cpp:237 gui/options.cpp:951
-#, fuzzy
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "Volume"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "Ignora le impostazioni globali di volume"
-#: gui/launcher.cpp:242
-#, fuzzy
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr "Ignora le impostazioni globali di volume"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "Ignora le impostazioni MIDI globali"
-#: gui/launcher.cpp:254
-#, fuzzy
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr "Ignora le impostazioni MIDI globali"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
-msgstr ""
+msgstr "MT-32"
-#: gui/launcher.cpp:267
-#, fuzzy
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
-msgstr "Ignora le impostazioni MIDI globali"
+msgstr "Ignora le impostazioni MT-32 globali"
-#: gui/launcher.cpp:269
-#, fuzzy
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
-msgstr "Ignora le impostazioni MIDI globali"
+msgstr "Ignora le impostazioni MT-32 globali"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
msgid "Paths"
msgstr "Percorsi"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
+msgid "Paths"
+msgstr "Perc."
+
+#: gui/launcher.cpp:295
msgid "Game Path:"
msgstr "Percorso gioco:"
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:297
+msgctxt "lowres"
+msgid "Game Path:"
+msgstr "Perc. gioco:"
+
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "Percorso extra:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "Specifica il percorso di ulteriori dati usati dal gioco"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "Perc. extra:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "Salvataggi:"
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "Specifica dove archiviare i salvataggi"
-#: gui/launcher.cpp:296
-#, fuzzy
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "Salvataggi:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
-#, fuzzy
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "Nessuno"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "Predefinito"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "Seleziona SoundFont"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "Seleziona la cartella contenente i file di gioco"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "Seleziona la cartella di gioco aggiuntiva"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "Seleziona la cartella dei salvataggi"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "Questo ID di gioco è già in uso. Si prega di sceglierne un'altro."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "C~h~iudi"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "Chiudi ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "~I~nfo..."
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "Informazioni su ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~O~pzioni..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "Modifica le opzioni globali di ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "~G~ioca"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "Esegue il gioco selezionato"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~C~arica..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "Carica un salvataggio del gioco selezionato"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~A~ggiungi gioco..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr "Tieni premuto Shift per l'aggiunta in massa"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "~M~odifica gioco..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "Modifica le opzioni di gioco"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "~R~imuovi gioco"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr "Rimuove il gioco dalla lista. I file del gioco rimarranno intatti"
-#: gui/launcher.cpp:563
-#, fuzzy
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
-msgstr "~A~ggiungi gioco..."
+msgstr "~A~gg. gioco..."
-#: gui/launcher.cpp:565
-#, fuzzy
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
-msgstr "~M~odifica gioco..."
+msgstr "~M~odif. gioco..."
-#: gui/launcher.cpp:567
-#, fuzzy
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
-msgstr "~R~imuovi gioco"
+msgstr "~R~im. gioco"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "Cerca nella lista dei giochi"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "Cerca:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "Cancella"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "Carica gioco:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "Carica"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -415,67 +434,65 @@ msgstr ""
"Vuoi davvero eseguire il rilevatore di giochi in massa? Potrebbe aggiungere "
"un numero enorme di giochi."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "Sì"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "No"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "ScummVM non ha potuto aprire la cartella specificata!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "ScummVM non ha potuto trovare nessun gioco nella cartella specificata!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "Scegli il gioco:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "Sei sicuro di voler rimuovere questa configurazione di gioco?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr ""
"Questo gioco non supporta il caricamento di salvataggi dalla schermata di "
"avvio."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr ""
"ScummVM non ha potuto trovare un motore in grado di eseguire il gioco "
"selezionato!"
-#: gui/launcher.cpp:1044
-#, fuzzy
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
-msgstr "Agg. in massa..."
+msgstr "Agg. massa..."
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "Agg. in massa..."
-#: gui/launcher.cpp:1045
-#, fuzzy
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
-msgstr "Aggiungi gioco..."
+msgstr "Agg. gioco..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "Aggiungi gioco..."
@@ -543,8 +560,7 @@ msgid "48 kHz"
msgstr "48 kHz"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
-#, fuzzy
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "Nessuno"
@@ -573,38 +589,47 @@ msgstr "Correzione proporzioni"
msgid "Correct aspect ratio for 320x200 games"
msgstr "Corregge le proporzioni dei giochi 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
msgstr "Disp. preferito:"
-#: gui/options.cpp:666
-#, fuzzy
+#: gui/options.cpp:667
msgid "Music Device:"
-msgstr "Dispositivo GM:"
+msgstr "Dispositivo audio:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
"Specifica il dispositivo audio o l'emulatore della scheda audio preferiti"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr ""
"Specifica il dispositivo di output audio o l'emulatore della scheda audio"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "Disp. preferito:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "Disposit. audio:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "Emulatore AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "AdLib è utilizzato per la musica in molti giochi"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "Frequenza:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -612,56 +637,54 @@ msgstr ""
"Valori più alti restituiscono un suono di maggior qualità, ma potrebbero non "
"essere supportati dalla tua scheda audio"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "Dispositivo GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr "Specifica il dispositivo audio predefinito per l'output General MIDI"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr "SoundFont è supportato da alcune schede audio, Fluidsynth e Timidity"
-#: gui/options.cpp:738
-#, fuzzy
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "Modalità mista AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "Utilizza generazione di suono sia MIDI che AdLib"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "Guadagno MIDI:"
-#: gui/options.cpp:756
-#, fuzzy
+#: gui/options.cpp:759
msgid "MT-32 Device:"
-msgstr "Disposit. MT32:"
+msgstr "Disposit. MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"Specifica il dispositivo audio predefinito per l'output Roland MT-32/LAPC1/"
"CM32l/CM64"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "Roland MT-32 effettivo (disattiva emulazione GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -669,190 +692,193 @@ msgstr ""
"Seleziona se vuoi usare il dispositivo hardware audio compatibile con Roland "
"che è connesso al tuo computer"
-#: gui/options.cpp:763
-#, fuzzy
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
-msgstr "Roland MT-32 effettivo (disattiva emulazione GM)"
+msgid "True Roland MT-32 (no GM emulation)"
+msgstr "Roland MT-32 effettivo (disat.emul.GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "Attiva la modalità Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
"Disattiva la mappatura General MIDI per i giochi con colonna sonora Roland "
"MT-32"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "Testo e voci:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr "Voci"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "Sottotitoli"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "Entrambi"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr "Velocità testo:"
-#: gui/options.cpp:801
-#, fuzzy
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "Testo e voci:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr "Voci"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "Sub"
-#: gui/options.cpp:807
-#, fuzzy
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
-msgstr "Entrambi"
+msgstr "Entr."
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "Mostra i sottotitoli e attiva le voci"
-#: gui/options.cpp:809
-#, fuzzy
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "Velocità testo:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "Volume musica:"
-#: gui/options.cpp:827
-#, fuzzy
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr "Volume musica:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "Disattiva audio"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "Volume effetti:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "Volume degli effetti sonori"
-#: gui/options.cpp:839
-#, fuzzy
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr "Volume effetti:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr "Volume voci:"
-#: gui/options.cpp:849
-#, fuzzy
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
msgstr "Volume voci:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "Salvataggi:"
-
-#: gui/options.cpp:981
+#: gui/options.cpp:991
msgid "Theme Path:"
msgstr "Percorso tema:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:993
+msgctxt "lowres"
+msgid "Theme Path:"
+msgstr "Perc. tema:"
+
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr "Specifica il percorso di ulteriori dati usati dai giochi o da ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
msgid "Plugins Path:"
msgstr "Percorso plugin:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1006
+msgctxt "lowres"
+msgid "Plugins Path:"
+msgstr "Perc. plugin:"
+
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "Varie"
-#: gui/options.cpp:999
-#, fuzzy
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "Varie"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "Tema:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr "Renderer GUI:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "Autosalva:"
-#: gui/options.cpp:1019
-#, fuzzy
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr "Autosalva:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "Tasti"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr "Lingua GUI:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Lingua dell'interfaccia grafica di ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr "Devi riavviare ScummVM affinché le modifiche abbiano effetto."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "Seleziona la cartella per i salvataggi"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr "La cartella scelta è in sola lettura. Si prega di sceglierne un'altra."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr "Seleziona la cartella dei temi dell'interfaccia"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "Seleziona la cartella dei file aggiuntivi"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "Seleziona la cartella dei plugin"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr "Nessuna data salvata"
@@ -893,30 +919,31 @@ msgstr "Salvataggio senza titolo"
msgid "Select a Theme"
msgstr "Seleziona un tema"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "Grafica disattivata"
-#: gui/ThemeEngine.cpp:335
-msgid "Standard Renderer (16bpp)"
-msgstr "Renderer standard (16bpp)"
-
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
-msgstr "Renderer con antialiasing (16bpp)"
-
-#: gui/ThemeEngine.cpp:341
-#, fuzzy
+#: gui/ThemeEngine.cpp:332
msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "Grafica disattivata"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "Renderer standard (16bpp)"
-#: gui/ThemeEngine.cpp:342
-#, fuzzy
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
+msgstr "Standard (16bpp)"
+
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr "Renderer con antialiasing (16bpp)"
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr "Con antialiasing (16bpp)"
+
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
@@ -941,11 +968,11 @@ msgstr "Pausa"
msgid "Skip line"
msgstr "Salta battuta"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "Errore nell'esecuzione del gioco:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr ""
"Impossibile trovare un motore in grado di eseguire il gioco selezionato"
@@ -1011,13 +1038,11 @@ msgid "Hercules Amber"
msgstr "Hercules ambra"
#: common/util.cpp:262
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Green"
msgstr "Hercules verde"
#: common/util.cpp:263
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Amber"
msgstr "Hercules ambra"
@@ -1048,19 +1073,21 @@ msgstr "~I~nfo"
#: engines/dialogs.cpp:110
msgid "~R~eturn to Launcher"
-msgstr "~V~ai a schermata di avvio"
+msgstr "~T~orna a elenco giochi"
#: engines/dialogs.cpp:112
-#, fuzzy
msgctxt "lowres"
msgid "~R~eturn to Launcher"
-msgstr "~V~ai a schermata di avvio"
+msgstr "~V~ai a elenco giochi"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "Salva gioco:"
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1095,6 +1122,39 @@ msgstr "~S~uccessivi"
msgid "~C~lose"
msgstr "~C~hiudi"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossibile salvare il gioco nel file:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"Impossibile caricare il gioco dal file:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"Gioco salvato con successo nel file:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "Modalità ~Z~ip attivata"
@@ -1107,6 +1167,14 @@ msgstr "~T~ransizioni attive"
msgid "~W~ater Effect Enabled"
msgstr "~E~ffetto acqua attivo"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "Ripristina gioco:"
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "Ripristina"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "Emulatore OPL MAME"
@@ -1120,9 +1188,8 @@ msgid "No music"
msgstr "Nessuna musica"
#: sound/mods/paula.cpp:192
-#, fuzzy
msgid "Amiga Audio Emulator"
-msgstr "Emulatore AdLib"
+msgstr "Emulatore audio Amiga"
#: sound/softsynth/adlib.cpp:1590
msgid "AdLib Emulator"
@@ -1130,12 +1197,11 @@ msgstr "Emulatore AdLib"
#: sound/softsynth/appleiigs.cpp:36
msgid "Apple II GS Emulator (NOT IMPLEMENTED)"
-msgstr ""
+msgstr "Emulatore Apple II GS (NON IMPLEMENTATO)"
#: sound/softsynth/sid.cpp:1434
-#, fuzzy
msgid "C64 Audio Emulator"
-msgstr "Emulatore AdLib"
+msgstr "Emulatore audio C64"
#: sound/softsynth/mt32.cpp:327
msgid "Initialising MT-32 Emulator"
@@ -1249,11 +1315,11 @@ msgstr "Audio ad alta qualità (più lento) (riavviare)"
msgid "Disable power off"
msgstr "Disattiva spegnimento in chiusura"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "Modalità touchpad attivata."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "Modalità touchpad disattivata."
@@ -1264,10 +1330,9 @@ msgid "Normal (no scaling)"
msgstr "Normale (nessun ridimensionamento)"
#: backends/platform/sdl/graphics.cpp:59
-#, fuzzy
msgctxt "lowres"
msgid "Normal (no scaling)"
-msgstr "Normale (nessun ridimensionamento)"
+msgstr "Normale (no ridim.)"
#: backends/platform/symbian/src/SymbianActions.cpp:41
#: backends/platform/wince/CEActionsSmartphone.cpp:38
@@ -1345,7 +1410,7 @@ msgstr "Tastiera virtuale"
msgid "Key mapper"
msgstr "Programmatore tasti"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "Sei sicuro di voler uscire?"
@@ -1475,7 +1540,7 @@ msgstr "Attesa per l'avvio della rete"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "Rete non avviata (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1561,10 +1626,32 @@ msgstr "Visualizza "
msgid "Do you want to perform an automatic scan ?"
msgstr "Vuoi eseguire una scansione automatica?"
+#: backends/platform/wince/wince-sdl.cpp:990
#, fuzzy
-#~ msgctxt "lowres"
-#~ msgid "Special sound effects volume"
-#~ msgstr "Volume degli effetti sonori"
+msgid "Map right click action"
+msgstr "Clic destro"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
-#~ msgid "English"
-#~ msgstr "Inglese"
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
diff --git a/po/module.mk b/po/module.mk
index c6109f69bc..30fcd0669d 100644
--- a/po/module.mk
+++ b/po/module.mk
@@ -38,8 +38,8 @@ updatepot:
translations-dat: tools/create_translations
tools/create_translations/create_translations $(POFILES)
- mv translations.dat gui/themes/
-
+ mv translations.dat $(srcdir)/gui/themes/
+
update-translations: updatepot $(POFILES) translations-dat
update-translations: updatepot $(POFILES)
diff --git a/po/ru_RU.po b/po/ru_RU.po
index ca31e4e409..45e011026d 100644
--- a/po/ru_RU.po
+++ b/po/ru_RU.po
@@ -1,13 +1,13 @@
# Russian translation for ScummVM.
-# Copyright (C) 2010 ScummVM
+# Copyright (C) 2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
# Eugene Sandulenko <sev@scummvm.org>, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM VERSION\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-08-31 20:55+0100\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
"PO-Revision-Date: 2010-06-13 20:55+0300\n"
"Last-Translator: Eugene Sandulenko <sev@scummvm.org>\n"
"Language-Team: Russian\n"
@@ -31,47 +31,56 @@ msgstr "²ÚÛîçÕÝÝëÕ Ò ÑØÛÔ ÞßæØØ:"
msgid "Available engines:"
msgstr "´ÞáâãßÝëÕ ÔÒØÖÚØ:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "²ÒÕàå"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "¿ÕàÕÙâØ ÝÐ ÔØàÕÚâÞàØî ãàÞÒÝÕÜ ÒëèÕ"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "²ÒÕàå"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "¾âÜÕÝÐ"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "²ëÑàÐâì"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "·ÐÚàëâì"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "ºÛØÚ Üëèìî"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "¿ÞÚÐ×Ðâì ÚÛÐÒØÐâãàã"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "¿ÕàÕÝÐ×ÝÐçØâì ÚÛÐÒØèØ"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "²ëÑÕàØâÕ ÔÕÙáâÒØÕ ÔÛï ÝÐ×ÝÐçÕÝØï"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "½Ð×ÝÐçØâì"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -99,19 +108,15 @@ msgstr "¿ÞÖÐÛãÙáâÐ, ÒëÑÕàØâÕ ÔÕÙáâÒØÕ"
msgid "Press the key to associate"
msgstr "½ÐÖÜØâÕ ÚÛÐÒØèã ÔÛï ÝÐ×ÝÐçÕÝØï"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "²ëÑÕàØâÕ ÔÕÙáâÒØÕ ÔÛï ÝÐ×ÝÐçÕÝØï"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "¸ÓàÐ"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "ID:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -119,19 +124,29 @@ msgstr ""
"ºÞàÞâÚØÙ ØÔÕÝâØäØÚÐâÞà, ØáßÞÛì×ãÕÜëÙ ÔÛï ØÜÕÝ áÞåàÐÝÕÝØÙ ØÓà Ø ÔÛï ×ÐßãáÚÐ "
"Ø× ÚÞÜÐÝÔÝÞÙ áâàÞÚØ"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
-msgstr "½Ð×Ò:"
+msgstr "½Ð×ÒÐÝØÕ:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "¿ÞÛÝÞÕ ÝÐ×ÒÐÝØÕ ØÓàë"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "½Ð×Ò:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "Ï×ëÚ:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -139,265 +154,282 @@ msgstr ""
"Ï×ëÚ ØÓàë. ¸×ÜÕÝÕÝØÕ íâÞÓÞ ßÐàÐÜÕâàÐ ÝÕ ßàÕÒàÐâØâ ØÓàã ÝÐ ÐÝÓÛØÙáÚÞÜ Ò "
"àãááÚãî"
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<ßÞ ãÜÞÛçÐÝØî>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "¿ÛÐâäÞàÜÐ:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "¿ÛÐâäÞàÜÐ, ÔÛï ÚÞâÞàÞÙ ØÓàÐ ÑëÛÐ Ø×ÝÐçÐÛìÝÞ àÐ×àÐÑÞâÐÝÐ"
-#: gui/launcher.cpp:197
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr "¿ÛÐâäÞàÜÐ:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "³àÐäØÚÐ"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "³àä"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÓàÐäØÚØ"
-#: gui/launcher.cpp:214
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÓàÐäØÚØ"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "°ãÔØÞ"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÐãÔØÞ"
-#: gui/launcher.cpp:226
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÐãÔØÞ"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "³àÞÜÚÞáâì"
-#: gui/launcher.cpp:237 gui/options.cpp:951
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "³àÞÜÚ"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÓàÞÜÚÞáâØ"
-#: gui/launcher.cpp:242
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ ÓàÞÜÚÞáâØ"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ MIDI"
-#: gui/launcher.cpp:254
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ MIDI"
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr "MT-32"
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ MT-32"
-#: gui/launcher.cpp:269
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr "¿ÕàÕÚàëâì ÓÛÞÑÐÛìÝëÕ ãáâÐÝÞÒÚØ MT-32"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
msgid "Paths"
msgstr "¿ãâØ"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
+msgid "Paths"
+msgstr "¿ãâØ"
+
+#: gui/launcher.cpp:295
+msgid "Game Path:"
+msgstr "¿ãâì Ú ØÓàÕ: "
+
+#: gui/launcher.cpp:297
+msgctxt "lowres"
msgid "Game Path:"
msgstr "³ÔÕ ØÓàÐ: "
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr "´Þß. ßãâì:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "ÃÚÐ×ëÒÐÕâ ßãâì Ú ÔÞßÞÛÝØâÕÛìÝëÜ äÐÙÛÐÜ ÔÐÝÝëå ÔÛï ØÓàë"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "´Þß. ßãâì:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "ÁÞåàÐÝÕÝØï ØÓà: "
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "ÃÚÐ×ëÒÐÕâ ßãâì Ú áÞåàÐÝÕÝØïÜ ØÓàë"
-#: gui/launcher.cpp:296
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "¿ãâì áÞåà:"
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "½Õ ×ÐÔÐÝ"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "¿Þ ãÜÞÛçÐÝØî"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "²ëÑÕàØâÕ SoundFont"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî á äÐÙÛÐÜØ ØÓàë"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "²ëÑÕàØâÕ ÔÞßÞÛÝØâÕÛìÝãî ÔØàÕÚâÞàØî ØÓàë"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî ÔÛï áÞåàÐÝÕÝØÙ"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "ÍâÞâ ID ØÓàë ãÖÕ ØáßÞÛì×ãÕâáï. ¿ÞÖÐÛãÙáâÐ, ÒëÑÕàØâÕ ÔàãÓÞÙ."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~²~ëåÞÔ"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "²ëåÞÔ Ø× ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "¾ ß~à~ÞÓàÐÜÜÕ..."
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "¾ ßàÞÓàÐÜÜÕ ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~¾~ßæØØ..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "¸×ÜÕÝØâì ÓÛÞÑÐÛìÝëÕ ÞßæØØ ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "¿~ã~áÚ"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "·ÐßãáâØâì ÒëÑàÐÝÝãî ØÓàã"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~·~ÐÓàã×Øâì..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "·ÐÓàã×Øâì áÞåàÝÕÝØÕ ÔÛï ÒëÑàÐÝÝÞÙ ØÓàë"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~´~ÞÑ. ØÓàã..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr "ÃÔÕàÖØÒÐÙâÕ ÚÛÐÒØèã Shift ÔÛï âÞÓÞ, çâÞÑë ÔÞÑÐÒØâì ÝÕáÚÞÛìÚÞ ØÓà"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "¾~ß~æØØ ØÓàë..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "¸×ÜÕÝØâì ÞßæØØ ØÓàë"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "~Ã~ÔÐÛØâì ØÓàã"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr "ÃÔÐÛØâì ØÓàã Ø× áßØáÚÐ. ½Õ ãÔÐÛïÕâ ØÓàã á ÖÕáâÚÞÓÞ ÔØáÚÐ"
-#: gui/launcher.cpp:563
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr "~´~ÞÑ. ØÓàã..."
-#: gui/launcher.cpp:565
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr "¾~ß~æØØ ØÓàë..."
-#: gui/launcher.cpp:567
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr "~Ã~ÔÐÛØâì ØÓàã"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "¿ÞØáÚ Ò áßØáÚÕ ØÓà"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "¿ÞØáÚ:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "¾çØáâØâì ×ÝÐçÕÝØÕ"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "·ÐÓàã×Øâì ØÓàã:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "·ÐÓàã×Øâì"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -405,61 +437,61 @@ msgstr ""
"²ë ÔÕÙáâÒØâÕÛìÝÞ åÞâØâÕ ×ÐßãáâØâì ÔÕâÕÚâÞà ÒáÕå ØÓà? ÍâÞ ßÞâÕÝæØÐÛìÝÞ ÜÞÖÕâ "
"ÔÞÑÐÒØâì ÑÞÛìèÞÕ ÚÞÛØçÕáâÒÞ ØÓà."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "´Ð"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "½Õâ"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "ScummVM ÝÕ ÜÞÖÕâ ÞâÚàëâì ãÚÐ×ÐÝÝãî ÔØàÕÚâÞàØî!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "ScummVM ÝÕ ÜÞÖÕâ ÝÐÙâØ ØÓàã Ò ãÚÐ×ÐÝÝÞÙ ÔØàÕÚâÞàØØ!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "²ëÑÕàØâÕ ØÓàã:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "²ë ÔÕÙáâÒØâÕÛìÝÞ åÞâØâÕ ãÔÐÛØâì ãáâÐÝÞÒÚØ ÔÛï íâÞÙ ØÓàë?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr "ÍâÐ ØÓàÐ ÝÕ ßÞÔÔÕàÖØÒÐÕâ ×ÐÓàã×Úã áÞåàÐÝÕÝØÙ çÕàÕ× ÓÛÐÒÝÞÕ ÜÕÝî."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr "ScummVM ÝÕ áÜÞÓ ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚÐ ÒëÑàÐÝÝÞÙ ØÓàë!"
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr "¼ÝÞÓÞ ØÓà..."
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "¼ÝÞÓÞ ØÓà..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr "½ÞÒÐï ØÓàÐ"
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr "½ÞÒÐï ØÓàÐ..."
@@ -527,7 +559,7 @@ msgid "48 kHz"
msgstr "48 Ú³æ"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "½Õ ×ÐÔÐÝ"
@@ -556,35 +588,46 @@ msgstr "ºÞààÕÚæØï áÞÞâÝÞèÕÝØï áâÞàÞÝ"
msgid "Correct aspect ratio for 320x200 games"
msgstr "ºÞààÕÚâØàÞÒÐâì áÞÞâÝÞèÕÝØÕ áâÞàÞÝ ÔÛï ØÓà á àÐ×àÕèÕÝØÕÜ 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
-msgstr "·ÒãÚÞÒÞÕ ãáâ-ÒÞ:"
+msgstr "¿àÕÔßÞçØâÐÕÜÞÕ:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Music Device:"
msgstr "·ÒãÚÞÒÞÕ ãáâ-ÒÞ:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
-msgstr "ÃÚÐ×ëÒÐÕâ ÒëåÞÔÝÞÕ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ ØÛØ íÜãÛïâÞà ×ÒãÚÞÒÞÙ ÚÐàâë"
+msgstr ""
+"ÃÚÐ×ëÒÐÕâ ßàÕÔßÞçØâÐÕÜÞÕ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ ØÛØ íÜãÛïâÞà ×ÒãÚÞÒÞÙ ÚÐàâë"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr "ÃÚÐ×ëÒÐÕâ ÒëåÞÔÝÞÕ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ ØÛØ íÜãÛïâÞà ×ÒãÚÞÒÞÙ ÚÐàâë"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "¿àÕÔßÞçØâÐÕÜÞÕ:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "·ÒãÚÞÒÞÕ ãáâ-ÒÞ:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "ÍÜãÛïâÞà AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "·ÒãÚÞÒÐï ÚÐàâÐ AdLib ØáßÞÛì×ãÕâáï ÜÝÞÓØÜØ ØÓàÐÜØ"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "ÇÐáâÞâÐ ×ÒãÚÐ:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -592,56 +635,56 @@ msgstr ""
"±¾ÛìèØÕ ×ÝÐçÕÝØï ×ÐÔÐîâ ÛãçèÕÕ ÚÐçÕáâÒÞ ×ÒãÚÐ, ÞÔÝÐÚÞ ÞÝØ ÜÞÓãâ ÝÕ "
"ßÞÔÔÕàÖØÒÐâìáï ÒÐèÕÙ ×ÒãÚÞÒÞÙ ÚÐàâÞÙ"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "ÃáâàÞÙáâÒÞ GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr "ÃÚÐ×ëÒÐÕâ ÒëåÞÔÝÞÕ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ ÔÛï MIDI"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
"SoundFontë ßÞÔÔÕàÔÖØÒÐîâáï ÝÕÚÞâÞàëÜØ ×ÒãÚÞÒëÜØ ÚÐàâÐÜØ, Fluidsynth Ø "
"Timidity"
-#: gui/options.cpp:738
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "ÁÜÕèÐÝÝëÙ àÕÖØÜ AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "¸áßÞÛì×ÞÒÐâì Ø MIDI Ø AdLib ÔÛï ÓÕÝÕàÐæØØ ×ÒãÚÐ"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "ÃáØÛÕÝØÕ MIDI:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "MT-32 Device:"
msgstr "Ãáâà. MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"ÃÚÐ×ëÒÐÕâ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ ßÞ ãÜÞÛçÐÝØï ÔÛï ÒëÒÞÔÐ ÝÐ Roland MT-32/LAPC1/"
"CM32l/CM64"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "½ÐáâÞïéØÙ Roland MT-32 (×ÐßàÕâØâì íÜãÛïæØî GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -649,182 +692,196 @@ msgstr ""
"¾âÜÕâìâÕ, ÕáÛØ ã ÒÐá ßÞÔÚÛîçÕÝÞ Roland-áÞÒÜÕáâØÜÞÕ ×ÒãÚÞÒÞÕ ãáâàÞÙáâÒÞ Ø Òë "
"åÞâØâÕ ÕÓÞ ØáßÞÛì×ÞÒÐâì"
-#: gui/options.cpp:763
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr "½ÐáâÞïéØÙ Roland MT-32 (×ÐßàÕâØâì GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "²ÚÛîçØâì àÕÖØÜ Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
"²ëÚÛîçÐÕâ ÜÐßßØÝÓ General MIDI ÔÛï ØÓà á ×ÒãÚÞÒÞÙ ÔÞàÞÖÚÞÙ ÔÛï Roland MT-32"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr "ÂÕÚáâ Ø Þ×ÒãçÚÐ:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr "¾×ÒãçÚÐ"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "ÁãÑâØâàë"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "²áñ"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr "ÁÚÞàÞáâì âØâàÞÒ:"
-#: gui/options.cpp:801
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr "ÂÕÚáâ Ø Þ×ÒãçÚÐ:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr "¾×Ò"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "狄"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr "²áñ"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "¿ÞÚÐ×ëÒÐâì áãÑâØâàë Ø ÒÞáßàÞØ×ÒÞÔØâì àÕçì"
-#: gui/options.cpp:809
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "ÁÚÞàÞáâì âØâàÞÒ:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "³àÞÜÚ. Üã×ëÚØ:"
-#: gui/options.cpp:827
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr "³àÞÜÚ. Üã×ëÚØ:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr "²ëÚÛ. Òáñ"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "³àÞÜÚÞáâì SFX:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "³àÞÜÚÞáâì áßÕæØÐÛìÝëå ×ÒãÚÞÒëå íääÕÚâÞÒ"
-#: gui/options.cpp:839
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr "³àÞÜÚ. SFX:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr "³àÞÜÚ. Þ×ÒãçÚØ:"
-#: gui/options.cpp:849
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
msgstr "³àÞÜÚ. Þ×ÒãçÚØ:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "ÁÞåàÐÝÕÝØï ØÓà:"
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "¿ãâì Ú âÕÜÐÜ:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr "³ÔÕ âÕÜë:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
"ÃÚÐ×ëÒÐÕâ ßãâì Ú ÔÞßÞÛÝØâÕÛìÝëÜ äÐÙÛÐÜ ÔÐÝÝëå, ØáßÞÛì×ãÕÜëå ÒáÕÜØ ØÓàÐÜØ, "
"ÛØÑÞ ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
+msgid "Plugins Path:"
+msgstr "¿ãâì Ú ßÛÐÓØÝÐÜ:"
+
+#: gui/options.cpp:1006
+msgctxt "lowres"
msgid "Plugins Path:"
msgstr "¿ãâì Ú ßÛÐÓØÝÐÜ:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "ÀÐ×ÝÞÕ"
-#: gui/options.cpp:999
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "ÀÐ×ÝÞÕ"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "ÂÕÜÐ:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr "ÀØáÞÒÐÛÚÐ GUI:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "°ÒâÞáÞåàÐÝÕÝØÕ:"
-#: gui/options.cpp:1019
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr "°ÒâÞáÞåà.:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "ºÛÐÒØèØ"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr "Ï×ëÚ GUI:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "Ï×ëÚ ÓàÐäØçÕáÚÞÓÞ ØÝâÕàäÕÙáÐ ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr "²ë ÔÞÛÖÝë ßÕàÕ×ÐßãáâØâì ScummVM çâÞÑë ßàØÜÕÝØâì Ø×ÜÕÝÕÝØï."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî ÔÛï áÞåàÐÝÕÝØÙ"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr "½Õ ÜÞÓã ßØáÐâì Ò ÒëÑàÐÝÝãî ÔØàÕÚâÞàØî. ¿ÞÖÐÛãÙáâÐ, ãÚÐÖØâÕ ÔàãÓãî."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî ÔÛï âÕÜ GUI"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî á ÔÞßÞÛÝØâÕÛìÝëÜØ äÐÙÛÐÜØ"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "²ëÑÕàØâÕ ÔØàÕÚâÞàØî á ßÛÐÓØÝÐÜØ"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+"ÂÕÜÐ, ÒëÑàÐÝÝÐï ÒÐÜØ, ÝÕ ßÞÔÔÕàÖØÒÐÕâ ÒëÑàÐÝÝëÙ ï×ëÚ. µáÛØ Òë åÞâØâÕ "
+"ØáßÞÛì×ÞÒÐâì íâã âÕÜã, ÒÐÜ ÝÕÞÑåÞÔØÜÞ áÝÐçÐÛÐ ßÕàÕÚÛîçØâìáï ÝÐ ÔàãÓÞÙ ï×ëÚ."
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr "´ÐâÐ ÝÕ ×ÐßØáÐÝÐ"
@@ -865,27 +922,30 @@ msgstr "ÁÞåàÐÝÕÝØÕ ÑÕ× ØÜÕÝØ"
msgid "Select a Theme"
msgstr "²ëÑÕàØâÕ âÕÜã"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "±Õ× ÓàÐäØÚØ"
-#: gui/ThemeEngine.cpp:335
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "±Õ× ÓàÐäØÚØ"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "ÁâÐÝÔÐàâÝëÙ àÐáâÕàØ×ÐâÞà (16bpp)"
-#: gui/ThemeEngine.cpp:337
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
+msgstr "ÁâÐÝÔÐàâÝëÙ àÐáâÕàØ×ÐâÞà (16bpp)"
+
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr "ÀÐáâÕàØ×ÐâÞà áÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)"
-#: gui/ThemeEngine.cpp:341
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
-msgstr "ÁâÐÝÔÐàâÝëÙ (16bpp)"
-
-#: gui/ThemeEngine.cpp:342
-msgctxt "lowres"
-msgid "Antialiased Renderer (16bpp)"
-msgstr "ÁÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)"
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr "ÀÐáâÕàØ×ÐâÞà áÞ áÓÛÐÖØÒÐÝØÕÜ (16bpp)"
#: base/main.cpp:205
#, c-format
@@ -911,11 +971,11 @@ msgstr "¿Ðã×Ð"
msgid "Skip line"
msgstr "¿àÞßãáâØâì áâàÞÚã"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "¾èØÑÚÐ ×ÐßãáÚÐ ØÓàë:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr "½Õ ÜÞÓã ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚÐ ÒëÑàÐÝÝÞÙ ØÓàë"
@@ -1022,11 +1082,14 @@ msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "~²~ ÓÛÐÒÝÞÕ ÜÕÝî"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "ÁÞåàÐÝØâì ØÓàã: "
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1061,6 +1124,39 @@ msgstr "~Á~ÛÕÔ"
msgid "~C~lose"
msgstr "~·~ÐÚàëâì"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"½Õ ãÔÐÛÞáì ×ÐßØáÐâì ØÓàã Ò äÐÙÛ:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"½Õ ãÔÐÛÞáì ×ÐÓàã×Øâì ØÓàã Ø× äÐÙÛÒÐ:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"¸ÓàÐ ãáßÕèÝÞ áÞåàÐÝÕÝÐ Ò äÐÙÛ:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "ÀÕÖØÜ ÑëáâàÞÓÞ ßÕàÕåÞÔÐ ÐÚâØÒØàÞÒÐÝ"
@@ -1073,6 +1169,14 @@ msgstr "¿ÕàÕåÞÔë ÐÚâØÒØàÞÒÐÝë"
msgid "~W~ater Effect Enabled"
msgstr "ÍääÕÚâë ÒÞÔë ÒÚÛîçÕÝë"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "²ÞááâÐÝÞÒØâì ØÓàã: "
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "²ÞááâÒÝÞÒØâì"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "ÍÜãÛïâÞà MAME OPL"
@@ -1213,11 +1317,11 @@ msgstr "²ëáÞÚÞÕ ÚÐçÕáâÒÞ ×ÒãÚÐ (ÜÕÔÛÕÝÝÕÕ) (àÕÑãâ)"
msgid "Disable power off"
msgstr "·ÐßàÕâØâì ÒëÚÛîçÕÝØÕ"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "ÀÕÖØÜ âÐçßÐÔÐ ÒÚÛîçÕÝ."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "ÀÕÖØÜ âÐçßÐÔÐ ÒëÚÛîçÕÝ."
@@ -1308,7 +1412,7 @@ msgstr "²ØàâãÐÛìÝÐï ÚÛÐÒØÐâãàÐ"
msgid "Key mapper"
msgstr "½Ð×ÝÐçÕÝØÕ ÚÛÐÒØè"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "²ë åÞâØâÕ ÒëÙâØ?"
@@ -1438,8 +1542,8 @@ msgstr "²àÕÜï ßÞÔÚÛîçÕÝØï Ú áÕâØ ØáâÕÚÛÞ"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
-msgstr "ÁÕâì ÝÕ ÝÐáâàÞÕÝÐ (%d)"
+msgid "Network not initialised (%d)"
+msgstr "ÁÕâì ÝÕ ÝÐáâàÞØÛÐáì (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
msgid "Hide Toolbar"
@@ -1523,3 +1627,34 @@ msgstr "¿ÞÚÐ×Ðâì "
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Do you want to perform an automatic scan ?"
msgstr "²ë åÞâØâÕ ßàÞØ×ÒÕáâØ ÐÒâÞÜÐâØçÕáÚØÙ ßÞØáÚ?"
+
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr "½Ð×ÝÐçØâì ÔÕÙáâÒØÕ ßÞ ßàÐÒÞÜã éÕÛçÚã"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr "²ë ÔÞÛÖÝë ÝÐ×ÝÐçØâì ÚÛÐÒØèã ÝÐ ÔÕÙáâÒØÕ 'Right Click' ÔÛï íâÞÙ ØÓàë"
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr "½Ð×ÝÐçØâì ÔÕÙáâÒØÕ 'áßàïâÐâì ßÐÝÕÛì ØÝáâàãÜÕÝâÞÒ'"
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr "²ë ÔÞÛÖÝë ÝÐ×ÝÐçØâì ÚÛÐÒØèã ÝÐ ÔÕÙâáâÒØÕ 'Hide toolbar' ÔÛï íâÞÙ ØÓàë"
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr "½Ð×ÝÐçØâì ÔÕÙáâÒØÕ ÃÒÕÛØçØâì ¼ÐáèâÐÑ (ÝÕÞÑï×ÐâÕÛìÝÞ)"
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr "½Ð×ÝÐçØâì ÔÕÙáâÒØÕ ÃÜÕÝìèØâì ¼ÐáèâÐÑ (ÝÕÞÑï×ÐâÕÛìÝÞ)"
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
+"½Õ ×ÐÑãÔìâÕ ÝÐ×ÝÐçØâì ÚÛÐÒØèã ÔÛï ÔÕÙáâÒØï 'Hide Toolbar' çâÞÑë ãÒØÔÕâì ÒÕáì "
+"ØÝÒÕÝâÐàì Ò ØÓàÕ"
diff --git a/po/scummvm.pot b/po/scummvm.pot
index a500dc0a4e..3bfc1cf91f 100644
--- a/po/scummvm.pot
+++ b/po/scummvm.pot
@@ -6,12 +6,13 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM 1.2.0svn\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -29,47 +30,56 @@ msgstr ""
msgid "Available engines:"
msgstr ""
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr ""
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr ""
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr ""
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr ""
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr ""
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr ""
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr ""
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr ""
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr ""
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr ""
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr ""
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -97,361 +107,384 @@ msgstr ""
msgid "Press the key to associate"
msgstr ""
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr ""
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr ""
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr ""
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
msgstr ""
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr ""
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr ""
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr ""
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr ""
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr ""
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
msgstr ""
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr ""
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr ""
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr ""
-#: gui/launcher.cpp:197
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr ""
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr ""
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr ""
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr ""
-#: gui/launcher.cpp:214
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr ""
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr ""
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr ""
-#: gui/launcher.cpp:226
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr ""
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr ""
-#: gui/launcher.cpp:237 gui/options.cpp:951
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr ""
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr ""
-#: gui/launcher.cpp:242
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr ""
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr ""
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr ""
-#: gui/launcher.cpp:254
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr ""
-#: gui/launcher.cpp:264 gui/options.cpp:965
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr ""
-#: gui/launcher.cpp:267
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr ""
-#: gui/launcher.cpp:269
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr ""
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr ""
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr ""
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
msgid "Game Path:"
msgstr ""
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:297
+msgctxt "lowres"
+msgid "Game Path:"
+msgstr ""
+
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
msgstr ""
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr ""
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr ""
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr ""
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr ""
-#: gui/launcher.cpp:296
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr ""
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr ""
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr ""
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr ""
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr ""
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr ""
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr ""
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr ""
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr ""
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr ""
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr ""
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr ""
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr ""
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr ""
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr ""
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr ""
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr ""
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr ""
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr ""
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr ""
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr ""
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr ""
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr ""
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr ""
-#: gui/launcher.cpp:563
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
msgstr ""
-#: gui/launcher.cpp:565
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr ""
-#: gui/launcher.cpp:567
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr ""
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr ""
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr ""
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr ""
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr ""
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr ""
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
msgstr ""
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr ""
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr ""
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr ""
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr ""
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr ""
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr ""
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr ""
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr ""
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr ""
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr ""
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
msgstr ""
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
msgstr ""
@@ -519,7 +552,7 @@ msgid "48 kHz"
msgstr ""
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr ""
@@ -548,264 +581,286 @@ msgstr ""
msgid "Correct aspect ratio for 320x200 games"
msgstr ""
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
msgstr ""
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Music Device:"
msgstr ""
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr ""
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr ""
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr ""
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr ""
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr ""
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr ""
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr ""
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
msgstr ""
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr ""
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr ""
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr ""
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
-#: gui/options.cpp:738
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr ""
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr ""
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr ""
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr ""
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "MT-32 Device:"
msgstr ""
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr ""
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
msgstr ""
-#: gui/options.cpp:763
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr ""
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr ""
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
msgstr ""
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
msgstr ""
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr ""
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr ""
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
msgstr ""
-#: gui/options.cpp:801
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
msgstr ""
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr ""
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr ""
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr ""
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr ""
-#: gui/options.cpp:809
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr ""
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr ""
-#: gui/options.cpp:827
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr ""
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
msgstr ""
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr ""
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr ""
-#: gui/options.cpp:839
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr ""
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
msgstr ""
-#: gui/options.cpp:849
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
msgstr ""
-#: gui/options.cpp:978
-msgid "Save Path: "
+#: gui/options.cpp:991
+msgid "Theme Path:"
msgstr ""
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr ""
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
+msgid "Plugins Path:"
+msgstr ""
+
+#: gui/options.cpp:1006
+msgctxt "lowres"
msgid "Plugins Path:"
msgstr ""
-#: gui/options.cpp:997
+#: gui/options.cpp:1015
msgid "Misc"
msgstr ""
-#: gui/options.cpp:999
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr ""
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr ""
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
msgstr ""
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr ""
-#: gui/options.cpp:1019
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr ""
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr ""
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr ""
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr ""
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr ""
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr ""
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr ""
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr ""
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr ""
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr ""
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr ""
@@ -846,28 +901,31 @@ msgstr ""
msgid "Select a Theme"
msgstr ""
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr ""
-#: gui/ThemeEngine.cpp:335
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
msgstr ""
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard Renderer (16bpp)"
msgstr ""
-#: gui/ThemeEngine.cpp:341
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
msgstr ""
-#: gui/ThemeEngine.cpp:342
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr ""
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr ""
+
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
@@ -892,11 +950,11 @@ msgstr ""
msgid "Skip line"
msgstr ""
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr ""
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr ""
@@ -1003,11 +1061,14 @@ msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr ""
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr ""
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1042,6 +1103,30 @@ msgstr ""
msgid "~C~lose"
msgstr ""
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr ""
@@ -1054,6 +1139,14 @@ msgstr ""
msgid "~W~ater Effect Enabled"
msgstr ""
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr ""
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr ""
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr ""
@@ -1194,11 +1287,11 @@ msgstr ""
msgid "Disable power off"
msgstr ""
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr ""
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr ""
@@ -1289,7 +1382,7 @@ msgstr ""
msgid "Key mapper"
msgstr ""
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr ""
@@ -1419,7 +1512,7 @@ msgstr ""
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr ""
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1504,3 +1597,32 @@ msgstr ""
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Do you want to perform an automatic scan ?"
msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:990
+msgid "Map right click action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
diff --git a/po/uk_UA.po b/po/uk_UA.po
index 1831859171..be646e78c1 100644
--- a/po/uk_UA.po
+++ b/po/uk_UA.po
@@ -1,22 +1,22 @@
# Ukrainian translation for ScummVM.
-# Copyright (C) 2010 ScummVM
+# Copyright (C) 2010 ScummVM Team
# This file is distributed under the same license as the ScummVM package.
-# Lubomyr Lisen , 2010.
+# Lubomyr Lisen, 2010.
#
msgid ""
msgstr ""
-"Project-Id-Version: ScummVM VERSION\n"
+"Project-Id-Version: ScummVM 1.3.0svn\n"
"Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n"
-"POT-Creation-Date: 2010-09-01 18:36+0300\n"
-"PO-Revision-Date: 2010-07-30 22:19+0100\n"
+"POT-Creation-Date: 2010-10-12 01:50+0200\n"
+"PO-Revision-Date: 2010-09-21 18:46+0200\n"
"Last-Translator: Lubomyr Lisen\n"
"Language-Team: Ukrainian\n"
+"Language: Ukrainian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-5\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Ukrainian\n"
-"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%"
-"10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n"
+"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#: gui/about.cpp:96
#, c-format
@@ -31,47 +31,56 @@ msgstr "²ÚÛîçÕÝö Ò ÑöÛÔ Þßæö÷:"
msgid "Available engines:"
msgstr "´ÞáâãßÝö ÔÒØÖÚØ:"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70
msgid "Go up"
msgstr "²ÒÕàå"
-#: gui/browser.cpp:69
+#: gui/browser.cpp:70 gui/browser.cpp:72
msgid "Go to previous directory level"
msgstr "¿ÕàÕÙâØ ÝÐ ßÐßÚã àöÒÝÕÜ ÒØéÕ"
-#: gui/browser.cpp:70 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
-#: gui/launcher.cpp:304 gui/massadd.cpp:95 gui/options.cpp:1066
+#: gui/browser.cpp:72
+msgctxt "lowres"
+msgid "Go up"
+msgstr "²ÒÕàå"
+
+#: gui/browser.cpp:73 gui/chooser.cpp:49 gui/KeysDialog.cpp:46
+#: gui/launcher.cpp:319 gui/massadd.cpp:95 gui/options.cpp:1084
#: gui/saveload.cpp:65 gui/saveload.cpp:157 gui/themebrowser.cpp:56
#: backends/platform/wii/options.cpp:48
msgid "Cancel"
msgstr "²öÔÜöÝÐ"
-#: gui/browser.cpp:71 gui/chooser.cpp:50 gui/themebrowser.cpp:57
+#: gui/browser.cpp:74 gui/chooser.cpp:50 gui/themebrowser.cpp:57
msgid "Choose"
msgstr "²ØÑàÐâØ"
-#: gui/GuiManager.cpp:103 backends/keymapper/remap-dialog.cpp:54
+#: gui/GuiManager.cpp:106 backends/keymapper/remap-dialog.cpp:54
msgid "Close"
msgstr "·ÐÚàØâØ"
-#: gui/GuiManager.cpp:106
+#: gui/GuiManager.cpp:109
msgid "Mouse click"
msgstr "ºÛöÚ ÜØèÚÞî"
-#: gui/GuiManager.cpp:109 base/main.cpp:285
+#: gui/GuiManager.cpp:112 base/main.cpp:285
msgid "Display keyboard"
msgstr "¿ÞÚÐ×ÐâØ ÚÛÐÒöÐâãàã"
-#: gui/GuiManager.cpp:112 base/main.cpp:288
+#: gui/GuiManager.cpp:115 base/main.cpp:288
msgid "Remap keys"
msgstr "¿ÕàÕßàØ×ÝÐçØâØ ÚÛÐÒöèö"
+#: gui/KeysDialog.h:39 gui/KeysDialog.cpp:148
+msgid "Choose an action to map"
+msgstr "²ØÑÕàöâì Ôöî ÔÛï ßàØ×ÝÐçÕÝÝï"
+
#: gui/KeysDialog.cpp:44
msgid "Map"
msgstr "¿àØ×ÝÐçØâØ"
-#: gui/KeysDialog.cpp:45 gui/launcher.cpp:305 gui/launcher.cpp:926
-#: gui/launcher.cpp:930 gui/massadd.cpp:92 gui/options.cpp:1067
+#: gui/KeysDialog.cpp:45 gui/launcher.cpp:320 gui/launcher.cpp:941
+#: gui/launcher.cpp:945 gui/massadd.cpp:92 gui/options.cpp:1085
#: backends/platform/wii/options.cpp:47
#: backends/platform/wince/CELauncherDialog.cpp:56
msgid "OK"
@@ -99,19 +108,15 @@ msgstr "±ãÔì ÛÐáÚÐ, ÒØÑÕàöâì Ôöî"
msgid "Press the key to associate"
msgstr "½ÐâØáÝöâì ÚÛÐÒöèã ÔÛï ßàØ×ÝÐçÕÝÝï"
-#: gui/KeysDialog.cpp:148
-msgid "Choose an action to map"
-msgstr "²ØÑÕàöâì Ôöî ÔÛï ßàØ×ÝÐçÕÝÝï"
-
#: gui/launcher.cpp:172
msgid "Game"
msgstr "³àÐ"
-#: gui/launcher.cpp:175
+#: gui/launcher.cpp:176
msgid "ID:"
msgstr "ID:"
-#: gui/launcher.cpp:175 gui/launcher.cpp:176
+#: gui/launcher.cpp:176 gui/launcher.cpp:178 gui/launcher.cpp:179
msgid ""
"Short game identifier used for referring to savegames and running the game "
"from the command line"
@@ -119,19 +124,29 @@ msgstr ""
"ºÞàÞâÚØÙ öÔÕÝâØäöÚÐâÞà, ïÚØÙ ÒØÚÞàØáâÞÒãôâìáï ÔÛï ÝÐ×Ò ×ÑÕàÕÖÕÝØå öÓÞà ö ÔÛï "
"×ÐßãáÚã × ÚÞÜÐÝÔÝÞ÷ áâàöçÚØ"
-#: gui/launcher.cpp:179
+#: gui/launcher.cpp:178
+msgctxt "lowres"
+msgid "ID:"
+msgstr "ID:"
+
+#: gui/launcher.cpp:183
msgid "Name:"
msgstr "½Ð×ÒÐ:"
-#: gui/launcher.cpp:179 gui/launcher.cpp:180
+#: gui/launcher.cpp:183 gui/launcher.cpp:185 gui/launcher.cpp:186
msgid "Full title of the game"
msgstr "¿ÞÒÝÐ ÝÐ×ÒÐ ÓàØ"
-#: gui/launcher.cpp:183
+#: gui/launcher.cpp:185
+msgctxt "lowres"
+msgid "Name:"
+msgstr "½Ð×ÒÐ:"
+
+#: gui/launcher.cpp:189
msgid "Language:"
msgstr "¼ÞÒÐ:"
-#: gui/launcher.cpp:183 gui/launcher.cpp:184
+#: gui/launcher.cpp:189 gui/launcher.cpp:190
msgid ""
"Language of the game. This will not turn your Spanish game version into "
"English"
@@ -139,279 +154,282 @@ msgstr ""
"¼ÞÒÐ ÓàØ. ·ÜöÝÐ æìÞÓÞ ßÐàÐÜÕâàã ÝÕ ßÕàÕâÒÞàØâì Óàã ÝÐ ÐÝÓÛöÙáìÚöÙ Ò "
"ãÚàÐ÷ÝáìÚã"
-#: gui/launcher.cpp:185 gui/launcher.cpp:199 gui/options.cpp:80
-#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1037
+#: gui/launcher.cpp:191 gui/launcher.cpp:205 gui/options.cpp:80
+#: gui/options.cpp:638 gui/options.cpp:648 gui/options.cpp:1055
#: sound/null.cpp:42
msgid "<default>"
msgstr "<×Ð ãÜÞÒçÐÝÝïÜ>"
-#: gui/launcher.cpp:195
+#: gui/launcher.cpp:201
msgid "Platform:"
msgstr "¿ÛÐâäÞàÜÐ:"
-#: gui/launcher.cpp:195 gui/launcher.cpp:197 gui/launcher.cpp:198
+#: gui/launcher.cpp:201 gui/launcher.cpp:203 gui/launcher.cpp:204
msgid "Platform the game was originally designed for"
msgstr "¿ÛÐâäÞàÜÐ, ÔÛï ïÚÞ÷ ÓàÐ ÑãÛÐ áßÞçÐâÚã àÞ×àÞÑÛÕÝÐ"
-#: gui/launcher.cpp:197
-#, fuzzy
+#: gui/launcher.cpp:203
msgctxt "lowres"
msgid "Platform:"
msgstr "¿ÛÐâäÞàÜÐ:"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "Graphics"
msgstr "³àÐäöÚÐ"
-#: gui/launcher.cpp:209 gui/options.cpp:921 gui/options.cpp:938
+#: gui/launcher.cpp:215 gui/options.cpp:924 gui/options.cpp:941
msgid "GFX"
msgstr "³àä"
-#: gui/launcher.cpp:212
+#: gui/launcher.cpp:218
msgid "Override global graphic settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÓàÐäöÚØ"
-#: gui/launcher.cpp:214
-#, fuzzy
+#: gui/launcher.cpp:220
msgctxt "lowres"
msgid "Override global graphic settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÓàÐäöÚØ"
-#: gui/launcher.cpp:221 gui/options.cpp:944
+#: gui/launcher.cpp:227 gui/options.cpp:947
msgid "Audio"
msgstr "°ãÔöÞ"
-#: gui/launcher.cpp:224
+#: gui/launcher.cpp:230
msgid "Override global audio settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÐãÔöÞ"
-#: gui/launcher.cpp:226
-#, fuzzy
+#: gui/launcher.cpp:232
msgctxt "lowres"
msgid "Override global audio settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÐãÔöÞ"
-#: gui/launcher.cpp:235 gui/options.cpp:949
+#: gui/launcher.cpp:241 gui/options.cpp:952
msgid "Volume"
msgstr "³ãçÝöáâì"
-#: gui/launcher.cpp:237 gui/options.cpp:951
-#, fuzzy
+#: gui/launcher.cpp:243 gui/options.cpp:954
msgctxt "lowres"
msgid "Volume"
msgstr "³ãçÝöáâì"
-#: gui/launcher.cpp:240
+#: gui/launcher.cpp:246
msgid "Override global volume settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÓãçÝÞáâö"
-#: gui/launcher.cpp:242
-#, fuzzy
+#: gui/launcher.cpp:248
msgctxt "lowres"
msgid "Override global volume settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ ÓãçÝÞáâö"
-#: gui/launcher.cpp:249 gui/options.cpp:959
+#: gui/launcher.cpp:255 gui/options.cpp:962
msgid "MIDI"
msgstr "MIDI"
-#: gui/launcher.cpp:252
+#: gui/launcher.cpp:258
msgid "Override global MIDI settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ MIDI"
-#: gui/launcher.cpp:254
-#, fuzzy
+#: gui/launcher.cpp:260
msgctxt "lowres"
msgid "Override global MIDI settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ MIDI"
-#: gui/launcher.cpp:264 gui/options.cpp:965
-#, fuzzy
+#: gui/launcher.cpp:270 gui/options.cpp:968
msgid "MT-32"
msgstr "MT-32"
-#: gui/launcher.cpp:267
-#, fuzzy
+#: gui/launcher.cpp:273
msgid "Override global MT-32 settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ MT-32"
-#: gui/launcher.cpp:269
-#, fuzzy
+#: gui/launcher.cpp:275
msgctxt "lowres"
msgid "Override global MT-32 settings"
msgstr "¿ÕàÕÚàØâØ ÓÛÞÑÐÛìÝö ãáâÐÝÞÒÚØ MT-32"
-#: gui/launcher.cpp:279 gui/options.cpp:971
+#: gui/launcher.cpp:286 gui/options.cpp:975
+msgid "Paths"
+msgstr "ÈÛïåØ"
+
+#: gui/launcher.cpp:288 gui/options.cpp:977
+msgctxt "lowres"
msgid "Paths"
msgstr "ÈÛïåØ"
-#: gui/launcher.cpp:285
+#: gui/launcher.cpp:295
+msgid "Game Path:"
+msgstr "ÈÛïå ÔÞ ÓàØ: "
+
+#: gui/launcher.cpp:297
+msgctxt "lowres"
msgid "Game Path:"
msgstr "ÈÛïå ÔÞ ÓàØ: "
-#: gui/launcher.cpp:289 gui/options.cpp:984
+#: gui/launcher.cpp:302 gui/options.cpp:997
msgid "Extra Path:"
-msgstr "´ÞÔ. èÛïå:"
+msgstr "´ÞÔÐâÚ. èÛïå:"
-#: gui/launcher.cpp:289 gui/launcher.cpp:290
+#: gui/launcher.cpp:302 gui/launcher.cpp:304 gui/launcher.cpp:305
msgid "Specifies path to additional data used the game"
msgstr "²ÚÐ×ãô èÛïå ÔÞ ÔÞÔÐâÚÞÒØå äÐÙÛöÒ ÔÐÝØå ÔÛï ÓàØ"
-#: gui/launcher.cpp:294
+#: gui/launcher.cpp:304 gui/options.cpp:999
+msgctxt "lowres"
+msgid "Extra Path:"
+msgstr "´ÞÔ. èÛïå:"
+
+#: gui/launcher.cpp:309 gui/options.cpp:985
msgid "Save Path:"
msgstr "ÈÛïå ×ÑÕà.: "
-#: gui/launcher.cpp:294 gui/launcher.cpp:296 gui/launcher.cpp:297
-#: gui/options.cpp:978 gui/options.cpp:979
+#: gui/launcher.cpp:309 gui/launcher.cpp:311 gui/launcher.cpp:312
+#: gui/options.cpp:985 gui/options.cpp:987 gui/options.cpp:988
msgid "Specifies where your savegames are put"
msgstr "²ÚÐ×ãô èÛïå ÔÞ ×ÑÕàÕÖÕÝì ÓàØ"
-#: gui/launcher.cpp:296
-#, fuzzy
+#: gui/launcher.cpp:311 gui/options.cpp:987
msgctxt "lowres"
msgid "Save Path:"
msgstr "ÈÛïå ×ÑÕà.: "
-#: gui/launcher.cpp:313 gui/launcher.cpp:393 gui/launcher.cpp:442
-#: gui/options.cpp:982 gui/options.cpp:985 gui/options.cpp:989
-#: gui/options.cpp:1090 gui/options.cpp:1096 gui/options.cpp:1102
-#: gui/options.cpp:1110 gui/options.cpp:1134 gui/options.cpp:1138
-#: gui/options.cpp:1144 gui/options.cpp:1151 gui/options.cpp:1250
-#, fuzzy
+#: gui/launcher.cpp:328 gui/launcher.cpp:408 gui/launcher.cpp:457
+#: gui/options.cpp:994 gui/options.cpp:1000 gui/options.cpp:1007
+#: gui/options.cpp:1108 gui/options.cpp:1114 gui/options.cpp:1120
+#: gui/options.cpp:1128 gui/options.cpp:1152 gui/options.cpp:1156
+#: gui/options.cpp:1162 gui/options.cpp:1169 gui/options.cpp:1268
msgctxt "path"
msgid "None"
msgstr "½Õ ×ÐÔÐÝØÙ"
-#: gui/launcher.cpp:318 gui/launcher.cpp:397
+#: gui/launcher.cpp:333 gui/launcher.cpp:412
#: backends/platform/wii/options.cpp:56
msgid "Default"
msgstr "·Ð ãÜÞÒçÐÝÝïÜ"
-#: gui/launcher.cpp:435 gui/options.cpp:1244
+#: gui/launcher.cpp:450 gui/options.cpp:1262
msgid "Select SoundFont"
msgstr "²ØÑÕàöâì SoundFont"
-#: gui/launcher.cpp:454 gui/launcher.cpp:601
+#: gui/launcher.cpp:469 gui/launcher.cpp:616
msgid "Select directory with game data"
msgstr "²ØÑÕàöâì ßÐßÚã × äÐÙÛÐÜØ ÓàØ"
-#: gui/launcher.cpp:472
+#: gui/launcher.cpp:487
msgid "Select additional game directory"
msgstr "²ØÑÕàöâì ÔÞÔÐâÚÞÒã ßÐßÚã ÓàØ"
-#: gui/launcher.cpp:484
+#: gui/launcher.cpp:499
msgid "Select directory for saved games"
msgstr "²ØÑÕàöâì ßÐßÚã ÔÛï ×ÑÕàÕÖÕÝì"
-#: gui/launcher.cpp:503
+#: gui/launcher.cpp:518
msgid "This game ID is already taken. Please choose another one."
msgstr "ÆÕÙ ID ÓàØ ÒÖÕ ÒØÚÞàØáâÞÒãôâìáï. ±ãÔì ÛÐáÚÐ, ÒØÑÕàöâì öÝèØÙ."
-#: gui/launcher.cpp:544 engines/dialogs.cpp:116
+#: gui/launcher.cpp:559 engines/dialogs.cpp:116
msgid "~Q~uit"
msgstr "~²~ØåöÔ"
-#: gui/launcher.cpp:544
+#: gui/launcher.cpp:559
msgid "Quit ScummVM"
msgstr "²ØåöÔ × ScummVM"
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "A~b~out..."
msgstr "¿àÞ ß~à~ÞÓàÐÜã..."
-#: gui/launcher.cpp:545
+#: gui/launcher.cpp:560
msgid "About ScummVM"
msgstr "¿àÞ ScummVM"
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "~O~ptions..."
msgstr "~¾~ßæö÷..."
-#: gui/launcher.cpp:546
+#: gui/launcher.cpp:561
msgid "Change global ScummVM options"
msgstr "·ÜöÝØâØ ÓÛÞÑÐÛìÝö Þßæö÷ ScummVM"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "~S~tart"
msgstr "·~Ð~ßãáÚ"
-#: gui/launcher.cpp:548
+#: gui/launcher.cpp:563
msgid "Start selected game"
msgstr "·ÐßãáâØâØ ÒØÑàÐÝã Óàã"
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "~L~oad..."
msgstr "~·~ÐÒÐÝ..."
-#: gui/launcher.cpp:551
+#: gui/launcher.cpp:566
msgid "Load savegame for selected game"
msgstr "·ÐÒÐÝâÐÖØâØ ×ÑÕàÕÖÕÝÝï ÔÛï ÒØÑàÐÝÞ÷ ÓàØ"
-#: gui/launcher.cpp:556
+#: gui/launcher.cpp:571
msgid "~A~dd Game..."
msgstr "~´~ÞÔ. Óàã..."
-#: gui/launcher.cpp:556 gui/launcher.cpp:563
+#: gui/launcher.cpp:571 gui/launcher.cpp:578
msgid "Hold Shift for Mass Add"
msgstr "ÃâàØÜãÙâÕ ÚÛÐÒöèã Shift ÔÛï âÞÓÞ, éÞÑ ÔÞÔÐâØ ÔÕÚöÛìÚÐ öÓÞà"
-#: gui/launcher.cpp:558
+#: gui/launcher.cpp:573
msgid "~E~dit Game..."
msgstr "ÀÕÔÐ~Ó~. Óàã..."
-#: gui/launcher.cpp:558 gui/launcher.cpp:565
+#: gui/launcher.cpp:573 gui/launcher.cpp:580
msgid "Change game options"
msgstr "·ÜöÝØâØ Þßæö÷ ÓàØ"
-#: gui/launcher.cpp:560
+#: gui/launcher.cpp:575
msgid "~R~emove Game"
msgstr "~²~ØÔÐÛØâØ Óàã"
-#: gui/launcher.cpp:560 gui/launcher.cpp:567
+#: gui/launcher.cpp:575 gui/launcher.cpp:582
msgid "Remove game from the list. The game data files stay intact"
msgstr "²ØÔÐÛØâØ Óàã ×ö áßØáÚã. ½Õ ÒØÔÐÛïô Óàã × ÖÞàáâÚÞÓÞ ÔØáÚÐ"
-#: gui/launcher.cpp:563
-#, fuzzy
+#: gui/launcher.cpp:578
msgctxt "lowres"
msgid "~A~dd Game..."
-msgstr "~´~ÞÔ. Óàã..."
+msgstr "~´~ÞÔÐâØ Óàã..."
-#: gui/launcher.cpp:565
-#, fuzzy
+#: gui/launcher.cpp:580
msgctxt "lowres"
msgid "~E~dit Game..."
msgstr "ÀÕÔÐ~Ó~. Óàã..."
-#: gui/launcher.cpp:567
-#, fuzzy
+#: gui/launcher.cpp:582
msgctxt "lowres"
msgid "~R~emove Game"
msgstr "~²~ØÔÐÛØâØ Óàã"
-#: gui/launcher.cpp:575
+#: gui/launcher.cpp:590
msgid "Search in game list"
msgstr "¿ÞèãÚ Ò áßØáÚã öÓÞà"
-#: gui/launcher.cpp:579 gui/launcher.cpp:1092
+#: gui/launcher.cpp:594 gui/launcher.cpp:1107
msgid "Search:"
msgstr "¿ÞèãÚ:"
-#: gui/launcher.cpp:582 gui/options.cpp:740
+#: gui/launcher.cpp:597 gui/options.cpp:743
msgid "Clear value"
msgstr "¾çØáâØâØ ×ÝÐçÕÝÝï"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
msgid "Load game:"
msgstr "·ÐÒÐÝâÐÖØâØ Óàã:"
-#: gui/launcher.cpp:604 engines/dialogs.cpp:120
+#: gui/launcher.cpp:619 engines/dialogs.cpp:120 engines/mohawk/myst.cpp:222
+#: engines/mohawk/riven.cpp:655 engines/cruise/menu.cpp:218
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:225
msgid "Load"
msgstr "·ÐÒÐÝâÐÖØâØ"
-#: gui/launcher.cpp:713
+#: gui/launcher.cpp:728
msgid ""
"Do you really want to run the mass game detector? This could potentially add "
"a huge number of games."
@@ -419,65 +437,63 @@ msgstr ""
"²Ø ÔöÙáÝÞ åÞçÕâÕ ×ÐßãáâØâØ ÔÕâÕÚâÞà ãáöå öÓÞà? ÆÕ ßÞâÕÝæöÙÝÞ ÜÞÖÕ ÔÞÔÐâØ "
"ÒÕÛØÚã ÚöÛìÚöáâì öÓÞà."
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "Yes"
msgstr "ÂÐÚ"
-#: gui/launcher.cpp:714 gui/launcher.cpp:863
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: gui/launcher.cpp:729 gui/launcher.cpp:878
+#: backends/platform/symbian/src/SymbianOS.cpp:450
#: backends/platform/wince/CEActionsPocket.cpp:313
#: backends/platform/wince/CEActionsSmartphone.cpp:272
#: backends/platform/wince/CELauncherDialog.cpp:104
msgid "No"
msgstr "½ö"
-#: gui/launcher.cpp:761
+#: gui/launcher.cpp:776
msgid "ScummVM couldn't open the specified directory!"
msgstr "ScummVM ÝÕ ÜÞÖÕ ÒöÔÚàØâØ ÒÚÐ×ÐÝã ßÐßÚã!"
-#: gui/launcher.cpp:773
+#: gui/launcher.cpp:788
msgid "ScummVM could not find any game in the specified directory!"
msgstr "ScummVM ÝÕ ÜÞÖÕ ×ÝÐÙâØ Óàã ã ÒÚÐ×ÐÝöÙ ßÐßæö!"
-#: gui/launcher.cpp:787
+#: gui/launcher.cpp:802
msgid "Pick the game:"
msgstr "²ØÑÕàöâì Óàã:"
-#: gui/launcher.cpp:863
+#: gui/launcher.cpp:878
msgid "Do you really want to remove this game configuration?"
msgstr "²Ø ÔöÙáÝÞ åÞçÕâÕ ÒØÔÐÛØâØ ãáâÐÝÞÒÚØ ÔÛï æöô÷ ÓàØ?"
-#: gui/launcher.cpp:926
+#: gui/launcher.cpp:941
msgid "This game does not support loading games from the launcher."
msgstr "Æï ÓàÐ ÝÕ ßöÔâàØÜãô ×ÐÒÐÝâÐÖÕÝÝï ×ÑÕàÕÖÕÝì çÕàÕ× ÓÞÛÞÒÝÕ ÜÕÝî."
-#: gui/launcher.cpp:930
+#: gui/launcher.cpp:945
msgid "ScummVM could not find any engine capable of running the selected game!"
msgstr "ScummVM ÝÕ ×ÜöÓ ×ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚã ÒØÑàÐÝÞ÷ ÓàØ!"
-#: gui/launcher.cpp:1044
-#, fuzzy
+#: gui/launcher.cpp:1059
msgctxt "lowres"
msgid "Mass Add..."
msgstr "´ÞÔ. ÑÐÓÐâÞ..."
-#: gui/launcher.cpp:1044
+#: gui/launcher.cpp:1059
msgid "Mass Add..."
msgstr "´ÞÔ. ÑÐÓÐâÞ..."
-#: gui/launcher.cpp:1045
-#, fuzzy
+#: gui/launcher.cpp:1060
msgctxt "lowres"
msgid "Add Game..."
-msgstr "´ÞÔ. Óàã..."
+msgstr "´ÞÔÐâØ Óàã..."
-#: gui/launcher.cpp:1045
+#: gui/launcher.cpp:1060
msgid "Add Game..."
-msgstr "´ÞÔ. Óàã..."
+msgstr "´ÞÔÐâØ Óàã..."
#: gui/massadd.cpp:79 gui/massadd.cpp:82
msgid "... progress ..."
@@ -543,19 +559,18 @@ msgid "48 kHz"
msgstr "48 Ú³æ"
#: gui/options.cpp:230 gui/options.cpp:399 gui/options.cpp:497
-#: gui/options.cpp:555 gui/options.cpp:739
-#, fuzzy
+#: gui/options.cpp:555 gui/options.cpp:742
msgctxt "soundfont"
msgid "None"
msgstr "½Õ ×ÐÔÐÝØÙ"
#: gui/options.cpp:635
msgid "Graphics mode:"
-msgstr "³àÐäöçÝØÙ àÕÖØÜ:"
+msgstr "³àÐäöçÝ. àÕÖØÜ:"
#: gui/options.cpp:646
msgid "Render mode:"
-msgstr "ÀÕÖØÜ àÐáâàãÒÐÝÝï:"
+msgstr "ÀÕÖØÜ àÐáâàãÒ.:"
#: gui/options.cpp:646 gui/options.cpp:647
msgid "Special dithering modes supported by some games"
@@ -573,36 +588,45 @@ msgstr "ºÞàÕÚæöï áßöÒÒöÔÝÞèÕÝÝï áâÞàöÝ"
msgid "Correct aspect ratio for 320x200 games"
msgstr "ºÞàØÓãÒÐâØ áßöÒÒöÔÝÞèÕÝÝï áâÞàöÝ ÔÛï öÓÞà × ÓàÐäöÚÞî 320x200"
-#: gui/options.cpp:666
+#: gui/options.cpp:667
msgid "Preferred Device:"
-msgstr "¿àØáâàöÙ ïÚÞÜã ÒöÔÔÐôâìáï ßÕàÕÒÐÓÐ:"
+msgstr "³ÞÛÞÒÝ. ßàØáâàöÙ:"
-#: gui/options.cpp:666
-#, fuzzy
+#: gui/options.cpp:667
msgid "Music Device:"
-msgstr "¼ã×ØçÝØÙ ¿àØáâàöÙ:"
+msgstr "¼ã×Øç. ¿àØáâàöÙ:"
-#: gui/options.cpp:666
+#: gui/options.cpp:667 gui/options.cpp:669
msgid "Specifies preferred sound device or sound card emulator"
msgstr "²ÚÐ×ãô ÒØåöÔÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ ÐÑÞ ÕÜãÛïâÞà ×ÒãÚÞÒÞ÷ ÚÐàâØ"
-#: gui/options.cpp:666 gui/options.cpp:667
+#: gui/options.cpp:667 gui/options.cpp:669 gui/options.cpp:670
msgid "Specifies output sound device or sound card emulator"
msgstr "²ÚÐ×ãô ÒØåöÔÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ ÐÑÞ ÕÜãÛïâÞà ×ÒãÚÞÒÞ÷ ÚÐàâØ"
-#: gui/options.cpp:692
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Preferred Dev.:"
+msgstr "¿àØáâàöÙ ïÚÞÜã ÒöÔÔÐôâìáï ßÕàÕÒÐÓÐ:"
+
+#: gui/options.cpp:669
+msgctxt "lowres"
+msgid "Music Device:"
+msgstr "¼ã×ØçÝØÙ ¿àØáâàöÙ:"
+
+#: gui/options.cpp:695
msgid "AdLib emulator:"
msgstr "µÜãÛïâÞà AdLib:"
-#: gui/options.cpp:692 gui/options.cpp:693
+#: gui/options.cpp:695 gui/options.cpp:696
msgid "AdLib is used for music in many games"
msgstr "·ÒãÚÞÒÐ ÚÐàâÐ AdLib ÒØÚÞàØáâÞÒãôâìáï ÑÐÓÐâìÜÐ öÓàÐÜØ"
-#: gui/options.cpp:703
+#: gui/options.cpp:706
msgid "Output rate:"
msgstr "²ØåöÔÝÐ çÐáâÞâÐ:"
-#: gui/options.cpp:703 gui/options.cpp:704
+#: gui/options.cpp:706 gui/options.cpp:707
msgid ""
"Higher value specifies better sound quality but may be not supported by your "
"soundcard"
@@ -610,57 +634,55 @@ msgstr ""
"²ÕÛØÚö ×ÝÐçÕÝÝï ×ÐÔÐîâì ÚàÐéã ïÚöáâì ×ÒãÚã, ßàÞâÕ ÒÞÝØ ÜÞÖãâì ÝÕ "
"ßöÔâàØÜãÒÐâØáï ÒÐèÞî ×ÒãÚÞÒÞî ÚÐàâÞî"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "GM Device:"
msgstr "¿àØáâàöÙ GM:"
-#: gui/options.cpp:714
+#: gui/options.cpp:717
msgid "Specifies default sound device for General MIDI output"
msgstr "²ÚÐ×ãô ÒØåöÔÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ ÔÛï MIDI"
-#: gui/options.cpp:736
+#: gui/options.cpp:739
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:736 gui/options.cpp:738 gui/options.cpp:739
+#: gui/options.cpp:739 gui/options.cpp:741 gui/options.cpp:742
msgid "SoundFont is supported by some audio cards, Fluidsynth and Timidity"
msgstr ""
"SoundFont ßöÔâàØÜãôâìáï ÔÕïÚØÜØ ×ÒãÚÞÒØÜØ ÚÐàâÐÜØ, Fluidsynth ö Timidity"
-#: gui/options.cpp:738
-#, fuzzy
+#: gui/options.cpp:741
msgctxt "lowres"
msgid "SoundFont:"
msgstr "SoundFont:"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Mixed AdLib/MIDI mode"
msgstr "·ÜöèÐÝØÙ àÕÖØÜ AdLib/MIDI"
-#: gui/options.cpp:743
+#: gui/options.cpp:746
msgid "Use both MIDI and AdLib sound generation"
msgstr "²ØÚÞàØáâÞÒãÒÐâØ ö MIDI ö AdLib ÔÛï ÓÕÝÕàÐæö÷ ×ÒãÚã"
-#: gui/options.cpp:746
+#: gui/options.cpp:749
msgid "MIDI gain:"
msgstr "¿ÞáØÛÕÝÝï MIDI:"
-#: gui/options.cpp:756
-#, fuzzy
+#: gui/options.cpp:759
msgid "MT-32 Device:"
msgstr "¿àØáâàöÙ MT-32:"
-#: gui/options.cpp:756
+#: gui/options.cpp:759
msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output"
msgstr ""
"²ÚÐ×ãô ×ÒãÚÞÒØÙ ßàØáâàöÙ ßÞ ãÜÞÒçÐÝÝî ÔÛï ÒØÒÞÔã ÝÐ Roland MT-32/LAPC1/CM32l/"
"CM64"
-#: gui/options.cpp:761
+#: gui/options.cpp:764
msgid "True Roland MT-32 (disable GM emulation)"
msgstr "ÁßàÐÒÖÝöÙ Roland MT-32 (ÒØÜÚÝãâØ ÕÜãÛïæØî GM)"
-#: gui/options.cpp:761 gui/options.cpp:763
+#: gui/options.cpp:764 gui/options.cpp:766
msgid ""
"Check if you want to use your real hardware Roland-compatible sound device "
"connected to your computer"
@@ -668,191 +690,194 @@ msgstr ""
"²öÔÜöâìâÕ, ïÚéÞ ã ÒÐá ßöÔÚÛîçÕÝØÙ Roland-áãÜöáÝØÙ ×ÒãÚÞÒØÙ ßàØáâàöÙ ö ÒØ "
"åÞçÕâÕ ÙÞÓÞ ÒØÚÞàØáâÐâØ"
-#: gui/options.cpp:763
-#, fuzzy
+#: gui/options.cpp:766
msgctxt "lowres"
-msgid "True Roland MT-32 (disable GM emulation)"
+msgid "True Roland MT-32 (no GM emulation)"
msgstr "ÁßàÐÒÖÝöÙ Roland MT-32 (ÒØÜÚÝãâØ ÕÜãÛïæØî GM)"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Enable Roland GS Mode"
msgstr "ÃÒöÜÚÝãâØ àÕÖØÜ Roland GS"
-#: gui/options.cpp:766
+#: gui/options.cpp:769
msgid "Turns off General MIDI mapping for games with Roland MT-32 soundtrack"
msgstr ""
"²ØÜØÚÐô ÜÐßßöÝÓ General MIDI ÔÛï öÓÞà ö× ×ÒãÚÞÒÞî ÔÞàöÖÚÞî ÔÛï Roland MT-32"
-#: gui/options.cpp:791
+#: gui/options.cpp:794
msgid "Text and Speech:"
-msgstr "ÂÕÚáâ ö Þ×ÒãçÕÝÝï:"
+msgstr "ÂÕÚáâ ö Þ×ÒãçÚÐ:"
-#: gui/options.cpp:795 gui/options.cpp:805
+#: gui/options.cpp:798 gui/options.cpp:808
msgid "Speech"
-msgstr "¾×ÒãçÕÝÝï"
+msgstr "¾×ÒãçÚÐ"
-#: gui/options.cpp:796 gui/options.cpp:806
+#: gui/options.cpp:799 gui/options.cpp:809
msgid "Subtitles"
msgstr "ÁãÑâØâàØ"
-#: gui/options.cpp:797
+#: gui/options.cpp:800
msgid "Both"
msgstr "²áÕ"
-#: gui/options.cpp:799
+#: gui/options.cpp:802
msgid "Subtitle speed:"
-msgstr "ÈÒØÔÚöáâì áãÑâØâàöÒ:"
+msgstr "ÈÒØÔ. áãÑâØâàöÒ:"
-#: gui/options.cpp:801
-#, fuzzy
+#: gui/options.cpp:804
msgctxt "lowres"
msgid "Text and Speech:"
-msgstr "ÂÕÚáâ ö Þ×ÒãçÕÝÝï:"
+msgstr "ÂÕÚáâ ö Þ×ÒãçÚÐ:"
-#: gui/options.cpp:805
+#: gui/options.cpp:808
msgid "Spch"
msgstr "¾×Ò"
-#: gui/options.cpp:806
+#: gui/options.cpp:809
msgid "Subs"
msgstr "狄"
-#: gui/options.cpp:807
-#, fuzzy
+#: gui/options.cpp:810
msgctxt "lowres"
msgid "Both"
msgstr "²áÕ"
-#: gui/options.cpp:807
+#: gui/options.cpp:810
msgid "Show subtitles and play speech"
msgstr "¿ÞÚÐ×ãÒÐâØ áãÑâØâàØ ö ÒöÔâÒÞàîÒÐâØ ÜÞÒã"
-#: gui/options.cpp:809
-#, fuzzy
+#: gui/options.cpp:812
msgctxt "lowres"
msgid "Subtitle speed:"
msgstr "ÈÒØÔÚöáâì áãÑâØâàöÒ:"
-#: gui/options.cpp:825
+#: gui/options.cpp:828
msgid "Music volume:"
msgstr "³ãçÝöáâì Üã×ØÚØ:"
-#: gui/options.cpp:827
-#, fuzzy
+#: gui/options.cpp:830
msgctxt "lowres"
msgid "Music volume:"
msgstr "³ãçÝöáâì Üã×ØÚØ:"
-#: gui/options.cpp:834
+#: gui/options.cpp:837
msgid "Mute All"
-msgstr "²ØÜÚÝãâØ ãáÕ"
+msgstr "²ØÜÚÝãâØ ÒáÕ"
-#: gui/options.cpp:837
+#: gui/options.cpp:840
msgid "SFX volume:"
msgstr "³ãçÝöáâì ÕäÕÚâöÒ:"
-#: gui/options.cpp:837 gui/options.cpp:839 gui/options.cpp:840
+#: gui/options.cpp:840 gui/options.cpp:842 gui/options.cpp:843
msgid "Special sound effects volume"
msgstr "³ãçÝöáâì áßÕæöÐÛìÝØå ×ÒãÚÞÒØå ÕäÕÚâöÒ"
-#: gui/options.cpp:839
-#, fuzzy
+#: gui/options.cpp:842
msgctxt "lowres"
msgid "SFX volume:"
msgstr "³ãçÝöáâì ÕäÕÚâöÒ:"
-#: gui/options.cpp:847
+#: gui/options.cpp:850
msgid "Speech volume:"
-msgstr "³ãçÝöáâì Þ×ÒãçÕÝÝï:"
+msgstr "³ãçÝöáâì Þ×ÒãçÚØ:"
-#: gui/options.cpp:849
-#, fuzzy
+#: gui/options.cpp:852
msgctxt "lowres"
msgid "Speech volume:"
-msgstr "³ãçÝöáâì Þ×ÒãçÕÝÝï:"
+msgstr "³ãçÝöáâì Þ×ÒãçÚØ:"
-#: gui/options.cpp:978
-msgid "Save Path: "
-msgstr "ÈÛïå ÔÛï ×ÑÕàÕÖÕÝì: "
+#: gui/options.cpp:991
+msgid "Theme Path:"
+msgstr "ÈÛïå ÔÞ âÕÜ:"
-#: gui/options.cpp:981
+#: gui/options.cpp:993
+msgctxt "lowres"
msgid "Theme Path:"
msgstr "ÈÛïå ÔÞ âÕÜ:"
-#: gui/options.cpp:984 gui/options.cpp:985
+#: gui/options.cpp:997 gui/options.cpp:999 gui/options.cpp:1000
msgid "Specifies path to additional data used by all games or ScummVM"
msgstr ""
"²ÚÐ×ãô èÛïå ÔÞ ÔÞÔÐâÚÞÒØå äÐÙÛöÒ ÔÐÝØå, ÒØÚÞàØáâÞÒãÒÐÝØå ãáöÜÐ öÓàÐÜØ, ÐÑÞ "
"ScummVM"
-#: gui/options.cpp:988
+#: gui/options.cpp:1004
+msgid "Plugins Path:"
+msgstr "ÈÛïå ÔÞ ßÛÐÓöÝöÒ:"
+
+#: gui/options.cpp:1006
+msgctxt "lowres"
msgid "Plugins Path:"
msgstr "ÈÛïå ÔÞ ßÛÐÓöÝöÒ:"
-#: gui/options.cpp:997
+#: gui/options.cpp:1015
msgid "Misc"
msgstr "Àö×ÝÕ"
-#: gui/options.cpp:999
-#, fuzzy
+#: gui/options.cpp:1017
msgctxt "lowres"
msgid "Misc"
msgstr "Àö×ÝÕ"
-#: gui/options.cpp:1001
+#: gui/options.cpp:1019
msgid "Theme:"
msgstr "ÂÕÜÐ:"
-#: gui/options.cpp:1005
+#: gui/options.cpp:1023
msgid "GUI Renderer:"
-msgstr "ÀÐáâÕàØ×ÐâÞà GUI:"
+msgstr "ÀÐáâÕàØ×Ðâ. GUI:"
-#: gui/options.cpp:1017
+#: gui/options.cpp:1035
msgid "Autosave:"
msgstr "°ÒâÞ×ÑÕàÕÖÕÝÝï:"
-#: gui/options.cpp:1019
-#, fuzzy
+#: gui/options.cpp:1037
msgctxt "lowres"
msgid "Autosave:"
msgstr "°ÒâÞ×ÑÕàÕÖÕÝÝï:"
-#: gui/options.cpp:1027
+#: gui/options.cpp:1045
msgid "Keys"
msgstr "ºÛÐÒöèö"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "GUI Language:"
msgstr "¼ÞÒÐ öÝâÕàäÕÙáã:"
-#: gui/options.cpp:1034
+#: gui/options.cpp:1052
msgid "Language of ScummVM GUI"
msgstr "¼ÞÒÐ ÓàÐäöçÝÞÓÞ öÝâÕàäÕÙáã ScummVM"
-#: gui/options.cpp:1183
+#: gui/options.cpp:1201
msgid "You have to restart ScummVM to take the effect."
msgstr "²Ø ßÞÒØÝÝö ßÕàÕ×ÐßãáâØâØ ScummVM éÞÑ ×ÐáâÞáãÒÐâØ ×ÜöÝØ."
-#: gui/options.cpp:1196
+#: gui/options.cpp:1214
msgid "Select directory for savegames"
msgstr "²ØÑÕàöâì ßÐßÚã ÔÛï ×ÑÕàÕÖÕÝì"
-#: gui/options.cpp:1203
+#: gui/options.cpp:1221
msgid "The chosen directory cannot be written to. Please select another one."
msgstr "½Õ ÜÞÖã ßØáÐâØ ã ÒØÑàÐÝã ßÐßÚã. ±ãÔì ÛÐáÚÐ, ÒÚÐÖöâì öÝèã."
-#: gui/options.cpp:1212
+#: gui/options.cpp:1230
msgid "Select directory for GUI themes"
msgstr "²ØÑÕàöâì ßÐßÚã ÔÛï âÕÜ GUI"
-#: gui/options.cpp:1222
+#: gui/options.cpp:1240
msgid "Select directory for extra files"
msgstr "²ØÑÕàöâì ßÐßÚã × ÔÞÔÐâÚÞÒØÜØ äÐÙÛÐÜØ"
-#: gui/options.cpp:1233
+#: gui/options.cpp:1251
msgid "Select directory for plugins"
msgstr "²ØÑÕàöâì ßÐßÚã × ßÛÐÓØÝÐÜØ"
+#: gui/options.cpp:1293
+msgid ""
+"The theme you selected does not support your current language. If you want "
+"to use this theme you need to switch to another language first."
+msgstr ""
+
#: gui/saveload.cpp:60 gui/saveload.cpp:241
msgid "No date saved"
msgstr "´ÐâÐ ÝÕ ×ÐßØáÐÝÐ"
@@ -893,30 +918,31 @@ msgstr "·ÑÕàÕÖÕÝÝï ÑÕ× öÜÕÝö"
msgid "Select a Theme"
msgstr "²ØÑÕàöâì âÕÜã"
-#: gui/ThemeEngine.cpp:334
+#: gui/ThemeEngine.cpp:332
msgid "Disabled GFX"
msgstr "±Õ× ÓàÐäöÚØ"
-#: gui/ThemeEngine.cpp:335
+#: gui/ThemeEngine.cpp:332
+msgctxt "lowres"
+msgid "Disabled GFX"
+msgstr "±Õ× ÓàÐäöÚØ"
+
+#: gui/ThemeEngine.cpp:333
msgid "Standard Renderer (16bpp)"
msgstr "ÁâÐÝÔÐàâÝØÙ àÐáâÕàØ×ÐâÞà (16bpp)"
-#: gui/ThemeEngine.cpp:337
-msgid "Antialiased Renderer (16bpp)"
-msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)"
-
-#: gui/ThemeEngine.cpp:341
-#, fuzzy
-msgctxt "lowres"
-msgid "Standard Renderer (16bpp)"
+#: gui/ThemeEngine.cpp:333
+msgid "Standard (16bpp)"
msgstr "ÁâÐÝÔÐàâÝØÙ àÐáâÕàØ×ÐâÞà (16bpp)"
-#: gui/ThemeEngine.cpp:342
-#, fuzzy
-msgctxt "lowres"
+#: gui/ThemeEngine.cpp:335
msgid "Antialiased Renderer (16bpp)"
msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)"
+#: gui/ThemeEngine.cpp:335
+msgid "Antialiased (16bpp)"
+msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)"
+
#: base/main.cpp:205
#, c-format
msgid "Engine does not support debug level '%s'"
@@ -941,11 +967,11 @@ msgstr "¿Ðã×Ð"
msgid "Skip line"
msgstr "¿àÞßãáâØâØ àïÔÞÚ"
-#: base/main.cpp:404
+#: base/main.cpp:401
msgid "Error running game:"
msgstr "¿ÞÜØÛÚÐ ×ÐßãáÚã ÓàØ:"
-#: base/main.cpp:430 base/main.cpp:431
+#: base/main.cpp:427 base/main.cpp:428
msgid "Could not find any engine capable of running the selected game"
msgstr "½Õ ÜÞÖã ×ÝÐÙâØ ÔÒØÖÞÚ ÔÛï ×ÐßãáÚã ÒØÑàÐÝÞ÷ ÓàØ"
@@ -1010,13 +1036,11 @@ msgid "Hercules Amber"
msgstr "Hercules ÏÝâÐàÝØÙ"
#: common/util.cpp:262
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Green"
msgstr "Hercules ·ÕÛÕÝØÙ"
#: common/util.cpp:263
-#, fuzzy
msgctxt "lowres"
msgid "Hercules Amber"
msgstr "Hercules ÏÝâÐàÝØÙ"
@@ -1047,19 +1071,21 @@ msgstr "¿àÞ ßàÞ~Ó~àÐÜã"
#: engines/dialogs.cpp:110
msgid "~R~eturn to Launcher"
-msgstr "~¿~ÞÒÕàÝãâØáì Ò ÓÞÛÞÒÝÕ ÜÕÝî"
+msgstr "~¿~ÞÒÕà. Ò ÓÞÛÞÒÝÕ ÜÕÝî"
#: engines/dialogs.cpp:112
-#, fuzzy
msgctxt "lowres"
msgid "~R~eturn to Launcher"
msgstr "~¿~ÞÒÕàÝãâØáì Ò ÓÞÛÞÒÝÕ ÜÕÝî"
-#: engines/dialogs.cpp:122
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
msgid "Save game:"
msgstr "·ÑÕàÕÓâØ Óàã: "
-#: engines/dialogs.cpp:122 backends/platform/symbian/src/SymbianActions.cpp:47
+#: engines/dialogs.cpp:122 engines/cruise/menu.cpp:216
+#: engines/sci/engine/kfile.cpp:577
+#: backends/platform/symbian/src/SymbianActions.cpp:47
#: backends/platform/wince/CEActionsPocket.cpp:42
#: backends/platform/wince/CEActionsPocket.cpp:263
#: backends/platform/wince/CEActionsSmartphone.cpp:44
@@ -1094,6 +1120,39 @@ msgstr "~½~Ðáâ"
msgid "~C~lose"
msgstr "~·~ÐÚàØâØ"
+#: engines/scumm/scumm.cpp:2248 engines/agos/saveload.cpp:192
+#, c-format
+msgid ""
+"Failed to save game state to file:\n"
+"\n"
+"%s"
+msgstr ""
+"½Õ ÒÔÐÛÞáï ×ÑÕàÕÓâØ áâÐÝ ÓàØ Ò äÐÙÛ:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2255 engines/agos/saveload.cpp:157
+#, c-format
+msgid ""
+"Failed to load game state from file:\n"
+"\n"
+"%s"
+msgstr ""
+"½Õ ÒÔÐÛÞáï ×ÐÒÐÝâÐÖØâØ áâÐÝ ÓàØ × äÐÙÛã:\n"
+"\n"
+"%s"
+
+#: engines/scumm/scumm.cpp:2267 engines/agos/saveload.cpp:200
+#, c-format
+msgid ""
+"Successfully saved game state in file:\n"
+"\n"
+"%s"
+msgstr ""
+"ÃáßöèÝÞ ×ÑÕàÖÕÝÞ áâÐÝ ÓàØ ã äÐÙÛö:\n"
+"\n"
+"%s"
+
#: engines/mohawk/dialogs.cpp:81 engines/mohawk/dialogs.cpp:115
msgid "~Z~ip Mode Activated"
msgstr "ÀÕÖØÜ èÒØÔÚÞÓÞ ßÕàÕåÞÔã ÐÚâØÒÞÒÐÝØÙ"
@@ -1106,6 +1165,14 @@ msgstr "¿ÕàÕåÞÔØ ÐÚâØÒÞÒÐÝö"
msgid "~W~ater Effect Enabled"
msgstr "µäÕÚâØ ÒÞÔØ ÒÚÛîçÕÝö"
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore game:"
+msgstr "²öÔÝÞÒØâØ Óàã: "
+
+#: engines/sci/engine/kfile.cpp:677
+msgid "Restore"
+msgstr "²öÔÝÞÒØâØ"
+
#: sound/fmopl.cpp:51
msgid "MAME OPL emulator"
msgstr "µÜãÛïâÞà MAME OPL:"
@@ -1119,9 +1186,8 @@ msgid "No music"
msgstr "±Õ× Üã×ØÚØ"
#: sound/mods/paula.cpp:192
-#, fuzzy
msgid "Amiga Audio Emulator"
-msgstr "µÜãÛïâÞà AdLib"
+msgstr "°ÜöÓÐ °ãÔöÞ µÜãÛïâÞà"
#: sound/softsynth/adlib.cpp:1590
msgid "AdLib Emulator"
@@ -1129,12 +1195,11 @@ msgstr "µÜãÛïâÞà AdLib"
#: sound/softsynth/appleiigs.cpp:36
msgid "Apple II GS Emulator (NOT IMPLEMENTED)"
-msgstr ""
+msgstr "Apple II GS µÜãÛïâÞà (½µ Àµ°»¦·¾²°½¾)"
#: sound/softsynth/sid.cpp:1434
-#, fuzzy
msgid "C64 Audio Emulator"
-msgstr "µÜãÛïâÞà AdLib"
+msgstr "C64 °ãÔöÞ µÜãÛïâÞà"
#: sound/softsynth/mt32.cpp:327
msgid "Initialising MT-32 Emulator"
@@ -1248,11 +1313,11 @@ msgstr "²ØáÞÚÐ ïÚöáâì ×ÒãÚã (ßÞÒöÛìÝöèÕ) (àÕÑãâ)"
msgid "Disable power off"
msgstr "·ÐÑÞàÞÝØâØ ÒØÜÚÝÕÝÝï"
-#: backends/platform/iphone/osys_events.cpp:339
+#: backends/platform/iphone/osys_events.cpp:357
msgid "Touchpad mode enabled."
msgstr "ÀÕÖØÜ âÐçßÐÔã ãÒöÜÚÝÕÝØÙ."
-#: backends/platform/iphone/osys_events.cpp:341
+#: backends/platform/iphone/osys_events.cpp:359
msgid "Touchpad mode disabled."
msgstr "ÀÕÖØÜ âÐçßÐÔã ÒØÜÚÝÕÝØÙ."
@@ -1263,7 +1328,6 @@ msgid "Normal (no scaling)"
msgstr "±Õ× ×ÑöÛìèÕÝÝï"
#: backends/platform/sdl/graphics.cpp:59
-#, fuzzy
msgctxt "lowres"
msgid "Normal (no scaling)"
msgstr "±Õ× ×ÑöÛìèÕÝÝï"
@@ -1344,7 +1408,7 @@ msgstr "²öàâãÐÛìÝÐ ÚÛÐÒöÐâãàÐ"
msgid "Key mapper"
msgstr "¿àØ×ÝÐçÕÝÝï ÚÛÐÒöè"
-#: backends/platform/symbian/src/SymbianOS.cpp:446
+#: backends/platform/symbian/src/SymbianOS.cpp:450
msgid "Do you want to quit ?"
msgstr "²Ø åÞçØâÕ ÒØÙâØ?"
@@ -1474,7 +1538,7 @@ msgstr "ÇÐá ßöÔÚÛîçÕÝÝï ÔÞ ÜÕàÕÖö ÒØâöÚ"
#: backends/platform/wii/options.cpp:186
#, c-format
-msgid "Network not initialsed (%d)"
+msgid "Network not initialised (%d)"
msgstr "¼ÕàÕÖÐ ÝÕ ÝÐÛÐÓÞÔÖÕÝÐ (%d)"
#: backends/platform/wince/CEActionsPocket.cpp:45
@@ -1560,10 +1624,55 @@ msgstr "¿ÞÚÐ×ÐâØ "
msgid "Do you want to perform an automatic scan ?"
msgstr "²Ø åÞçÕâÕ ×ÔöÙáÝØâØ ÐÒâÞÜÐâØçÝØÙ ßÞèãÚ?"
+#: backends/platform/wince/wince-sdl.cpp:990
+#, fuzzy
+msgid "Map right click action"
+msgstr "¿àÐÒØÙ ÚÛöÚ"
+
+#: backends/platform/wince/wince-sdl.cpp:994
+msgid "You must map a key to the 'Right Click' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1003
+msgid "Map hide toolbar action"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1007
+msgid "You must map a key to the 'Hide toolbar' action to play this game"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1016
+msgid "Map Zoom Up action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1019
+msgid "Map Zoom Down action (optional)"
+msgstr ""
+
+#: backends/platform/wince/wince-sdl.cpp:1027
+msgid ""
+"Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"
+msgstr ""
+
+#~ msgctxt "lowres"
+#~ msgid "Preferred Device:"
+#~ msgstr "¿àØáâàöÙ ïÚÞÜã ÒöÔÔÐôâìáï ßÕàÕÒÐÓÐ:"
+
+#~ msgctxt "lowres"
+#~ msgid "True Roland MT-32 (disable GM emulation)"
+#~ msgstr "ÁßàÐÒÖÝöÙ Roland MT-32 (ÒØÜÚÝãâØ ÕÜãÛïæØî GM)"
+
+#, fuzzy
+#~ msgctxt "lowres"
+#~ msgid "Standard Renderer (16bpp)"
+#~ msgstr "ÁâÐÝÔÐàâÝØÙ àÐáâÕàØ×ÐâÞà (16bpp)"
+
+#, fuzzy
+#~ msgctxt "lowres"
+#~ msgid "Antialiased Renderer (16bpp)"
+#~ msgstr "ÀÐáâÕàØ×ÐâÞà ×ö ×ÓÛÐÔÖãÒÐÝÝïÜ (16bpp)"
+
#, fuzzy
#~ msgctxt "lowres"
#~ msgid "Special sound effects volume"
#~ msgstr "³ãçÝöáâì áßÕæöÐÛìÝØå ×ÒãÚÞÒØå ÕäÕÚâöÒ"
-
-#~ msgid "English"
-#~ msgstr "English"
diff --git a/ports.mk b/ports.mk
index 0a7f6c4d92..f9012eb0bd 100644
--- a/ports.mk
+++ b/ports.mk
@@ -66,7 +66,7 @@ endif
chmod 755 scummvm
cp scummvm $(bundle_name)/ScummVM
cp $(srcdir)/dists/iphone/icon.png $(bundle_name)/
- cp $(srcdir)/dists/iphone/icon-72.png $(bundle_name)/
+ cp $(srcdir)/dists/iphone/icon-72.png $(bundle_name)/
cp $(srcdir)/dists/iphone/Default.png $(bundle_name)/
# Location of static libs for the iPhone
@@ -98,6 +98,10 @@ ifdef USE_MPEG2
OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libmpeg2.a
endif
+ifdef USE_PNG
+OSX_STATIC_LIBS += $(STATICLIBPATH)/lib/libpng.a
+endif
+
ifdef USE_ZLIB
OSX_ZLIB ?= -lz
endif
@@ -187,12 +191,7 @@ aos4dist: $(EXECUTABLE)
ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/
endif
- cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt
- cp $(srcdir)/COPYING $(AOS4PATH)/COPYING.txt
- cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt
- cp $(srcdir)/COPYRIGHT $(AOS4PATH)/COPYRIGHT.txt
- cp $(srcdir)/NEWS $(AOS4PATH)/NEWS.txt
- cp $(srcdir)/README $(AOS4PATH)/README.txt
+ cp $(DIST_FILES_DOCS) $(AOS4PATH)
# Mark special targets as phony
.PHONY: deb bundle osxsnap win32dist install uninstall
diff --git a/sound/audiocd.cpp b/sound/audiocd.cpp
index 625def58fe..f1288131fa 100644
--- a/sound/audiocd.cpp
+++ b/sound/audiocd.cpp
@@ -120,7 +120,7 @@ void AudioCDManager::setVolume(byte volume) {
_mixer->setChannelVolume(_handle, _cd.volume);
} else {
// Real Audio CD
-
+
// Unfortunately I can't implement this atm
// since SDL doesn't seem to offer an interface method for this.
@@ -136,7 +136,7 @@ void AudioCDManager::setBalance(int8 balance) {
_mixer->setChannelBalance(_handle, _cd.balance);
} else {
// Real Audio CD
-
+
// Unfortunately I can't implement this atm
// since SDL doesn't seem to offer an interface method for this.
diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp
index c8a907d13e..8a27658e4b 100644
--- a/sound/decoders/adpcm.cpp
+++ b/sound/decoders/adpcm.cpp
@@ -290,8 +290,8 @@ int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
class MSIma_ADPCMStream : public Ima_ADPCMStream {
public:
- MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
- : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
+ MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign, bool invertSamples = false)
+ : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign), _invertSamples(invertSamples) {
if (blockAlign == 0)
error("ADPCMStream(): blockAlign isn't specified for MS IMA ADPCM");
}
@@ -305,6 +305,9 @@ public:
int readBufferMSIMA1(int16 *buffer, const int numSamples);
int readBufferMSIMA2(int16 *buffer, const int numSamples);
+
+private:
+ bool _invertSamples; // Some implementations invert the way samples are decoded
};
int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
@@ -324,8 +327,8 @@ int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
data = _stream->readByte();
_blockPos[0]++;
- buffer[samples] = decodeIMA(data & 0x0f);
- buffer[samples + 1] = decodeIMA((data >> 4) & 0x0f);
+ buffer[samples] = decodeIMA(_invertSamples ? (data >> 4) & 0x0f : data & 0x0f);
+ buffer[samples + 1] = decodeIMA(_invertSamples ? data & 0x0f : (data >> 4) & 0x0f);
}
}
return samples;
@@ -733,6 +736,8 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo
return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMMSIma:
return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
+ case kADPCMMSImaLastExpress:
+ return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign, true);
case kADPCMMS:
return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMTinsel4:
diff --git a/sound/decoders/adpcm.h b/sound/decoders/adpcm.h
index 04dbb1a521..edcdc01ce9 100644
--- a/sound/decoders/adpcm.h
+++ b/sound/decoders/adpcm.h
@@ -50,14 +50,15 @@ class RewindableAudioStream;
// Usually, if the audio stream we're trying to play has the FourCC header
// string intact, it's easy to discern which encoding is used
enum typesADPCM {
- kADPCMOki, // Dialogic/Oki ADPCM (aka VOX)
- kADPCMMSIma, // Microsoft IMA ADPCM
- kADPCMMS, // Microsoft ADPCM
- kADPCMTinsel4, // 4-bit ADPCM used by the Tinsel engine
- kADPCMTinsel6, // 6-bit ADPCM used by the Tinsel engine
- kADPCMTinsel8, // 8-bit ADPCM used by the Tinsel engine
- kADPCMIma, // Standard IMA ADPCM
- kADPCMApple // Apple QuickTime IMA ADPCM
+ kADPCMOki, // Dialogic/Oki ADPCM (aka VOX)
+ kADPCMMSIma, // Microsoft IMA ADPCM
+ kADPCMMSImaLastExpress, // Microsoft IMA ADPCM (with inverted samples)
+ kADPCMMS, // Microsoft ADPCM
+ kADPCMTinsel4, // 4-bit ADPCM used by the Tinsel engine
+ kADPCMTinsel6, // 6-bit ADPCM used by the Tinsel engine
+ kADPCMTinsel8, // 8-bit ADPCM used by the Tinsel engine
+ kADPCMIma, // Standard IMA ADPCM
+ kADPCMApple // Apple QuickTime IMA ADPCM
};
/**
diff --git a/sound/decoders/flac.cpp b/sound/decoders/flac.cpp
index e588aa872f..65f3306106 100644
--- a/sound/decoders/flac.cpp
+++ b/sound/decoders/flac.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides for FILE as that is used in FLAC headers
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+
#include "sound/decoders/flac.h"
#ifdef USE_FLAC
@@ -140,7 +143,7 @@ public:
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
- bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC ; }
+ bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; }
protected:
uint getChannels() const { return MIN<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
@@ -303,7 +306,7 @@ int FLACStream::readBuffer(int16 *buffer, const int numSamples) {
const uint numChannels = getChannels();
if (numChannels == 0) {
- warning("FLACStream: Stream not sucessfully initialised, cant playback");
+ warning("FLACStream: Stream not successfully initialised, cant playback");
return -1; // streaminfo wasnt read!
}
@@ -553,7 +556,7 @@ void FLACStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__i
for (; numSamples > 0; numSamples -= numChannels) {
for (uint i = 0; i < numChannels; ++i)
- *bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower) ;
+ *bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower);
}
} else {
for (; numSamples > 0; numSamples -= numChannels) {
diff --git a/sound/decoders/mac_snd.cpp b/sound/decoders/mac_snd.cpp
index d6894f1144..48f6886bf4 100644
--- a/sound/decoders/mac_snd.cpp
+++ b/sound/decoders/mac_snd.cpp
@@ -69,7 +69,7 @@ SeekableAudioStream *makeMacSndStream(Common::SeekableReadStream *stream,
// We really should never get this as long as we have sampled data only
if (stream->readUint16BE() != 1) {
warning("makeMacSndStream(): Unsupported command count");
- return 0;
+ return 0;
}
uint16 command = stream->readUint16BE();
diff --git a/sound/decoders/vorbis.cpp b/sound/decoders/vorbis.cpp
index 7673c53010..ee31bbc38d 100644
--- a/sound/decoders/vorbis.cpp
+++ b/sound/decoders/vorbis.cpp
@@ -23,6 +23,11 @@
*
*/
+// Disable symbol overrides for FILE and fseek as those are used in the
+// Vorbis headers.
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_fseek
+
#include "sound/decoders/vorbis.h"
#ifdef USE_VORBIS
diff --git a/sound/fmopl.cpp b/sound/fmopl.cpp
index 16dc3986d6..ee54a79a46 100644
--- a/sound/fmopl.cpp
+++ b/sound/fmopl.cpp
@@ -42,7 +42,7 @@ enum OplEmulator {
OPL::OPL() {
if (_hasInstance)
- error("There are multiple OPL output instances running.");
+ error("There are multiple OPL output instances running");
_hasInstance = true;
}
@@ -91,7 +91,7 @@ Config::DriverId Config::detect(OplType type) {
} else {
// Else we will output a warning and just
// return that no valid driver is found.
- warning("Your selected OPL driver \"%s\" does not support type %d emulation, which is requested by your game.", _drivers[drv].description, type);
+ warning("Your selected OPL driver \"%s\" does not support type %d emulation, which is requested by your game", _drivers[drv].description, type);
return -1;
}
}
@@ -138,7 +138,7 @@ OPL *Config::create(DriverId driver, OplType type) {
if (type == kOpl2)
return new MAME::OPL();
else
- warning("MAME OPL emulator only supports OPL2 emulation.");
+ warning("MAME OPL emulator only supports OPL2 emulation");
return 0;
#ifndef DISABLE_DOSBOX_OPL
diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp
index eb8bafee01..20d5a4e233 100644
--- a/sound/mididrv.cpp
+++ b/sound/mididrv.cpp
@@ -58,7 +58,7 @@ const byte MidiDriver::_gmToMt32[128] = {
static const uint32 GUIOMapping[] = {
MT_PCSPK, Common::GUIO_MIDIPCSPK,
- /*MDT_CMS, Common::GUIO_MIDICMS,*/
+ MT_CMS, Common::GUIO_MIDICMS,
MT_PCJR, Common::GUIO_MIDIPCJR,
MT_ADLIB, Common::GUIO_MIDIADLIB,
MT_C64, Common::GUIO_MIDIC64,
@@ -162,7 +162,7 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
case MT_AMIGA:
if (flags & MDT_AMIGA)
return hdl;
- break;
+ break;
case MT_APPLEIIGS:
if (flags & MDT_APPLEIIGS)
@@ -209,35 +209,40 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
hdl = getDeviceHandle("auto");
const MusicType type = getMusicType(hdl);
- if (type != MT_AUTO && type != MT_INVALID) {
- if (flags & MDT_PREFER_MT32)
- // If we have a preferred MT32 device we disable the gm/mt32 mapping (more about this in mididrv.h)
- _forceTypeMT32 = true;
- return hdl;
- }
+ // If have a "Don't use GM/MT-32" setting we skip this part and jump
+ // to AdLib, PC Speaker etc. detection right away.
+ if (type != MT_NULL) {
+ if (type != MT_AUTO && type != MT_INVALID) {
+ if (flags & MDT_PREFER_MT32)
+ // If we have a preferred MT32 device we disable the gm/mt32 mapping (more about this in mididrv.h)
+ _forceTypeMT32 = true;
+
+ return hdl;
+ }
+
+ // If we have no specific device selected (neither in the scummvm nor in the game domain)
+ // and no preferred MT32 or GM device selected we arrive here.
+ // If MT32 is preferred we try for the first available device with music type 'MT_MT32' (usually the mt32 emulator)
+ if (flags & MDT_PREFER_MT32) {
+ for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
+ MusicDevices i = (**m)->getDevices();
+ for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
+ if (d->getMusicType() == MT_MT32)
+ return d->getHandle();
+ }
+ }
+ }
- // If we have no specific device selected (neither in the scummvm nor in the game domain)
- // and no preferred MT32 or GM device selected we arrive here.
- // If MT32 is preferred we try for the first available device with music type 'MT_MT32' (usually the mt32 emulator)
- if (flags & MDT_PREFER_MT32) {
+ // Now we default to the first available device with music type 'MT_GM'
for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
MusicDevices i = (**m)->getDevices();
for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
- if (d->getMusicType() == MT_MT32)
+ if (d->getMusicType() == MT_GM || d->getMusicType() == MT_GS)
return d->getHandle();
}
}
}
-
- // Now we default to the first available device with music type 'MT_GM'
- for (MusicPlugin::List::const_iterator m = p.begin(); m != p.end(); ++m) {
- MusicDevices i = (**m)->getDevices();
- for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
- if (d->getMusicType() == MT_GM || d->getMusicType() == MT_GS)
- return d->getHandle();
- }
- }
}
MusicType tp = MT_AUTO;
@@ -247,10 +252,10 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
tp = MT_PC98;
else if (flags & MDT_ADLIB)
tp = MT_ADLIB;
- else if (flags & MDT_PCSPK)
- tp = MT_PCSPK;
else if (flags & MDT_PCJR)
tp = MT_PCJR;
+ else if (flags & MDT_PCSPK)
+ tp = MT_PCSPK;
else if (flags & MDT_C64)
tp = MT_C64;
else if (flags & MDT_AMIGA)
@@ -306,3 +311,16 @@ MidiDriver::DeviceHandle MidiDriver::getDeviceHandle(const Common::String &ident
return 0;
}
+
+void MidiDriver::sendMT32Reset() {
+ static const byte resetSysEx[] = { 0x41, 0x10, 0x16, 0x12, 0x7F, 0x00, 0x00, 0x01, 0x00 };
+ sysEx(resetSysEx, sizeof(resetSysEx));
+ g_system->delayMillis(100);
+}
+
+void MidiDriver::sendGMReset() {
+ static const byte resetSysEx[] = { 0x7E, 0x7F, 0x09, 0x01 };
+ sysEx(resetSysEx, sizeof(resetSysEx));
+ g_system->delayMillis(100);
+}
+
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 7ba1fe19f7..9e649cba3d 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -216,6 +216,16 @@ public:
}
/**
+ * Send a Roland MT-32 reset sysEx to the midi device.
+ */
+ void sendMT32Reset();
+
+ /**
+ * Send a General MIDI reset sysEx to the midi device.
+ */
+ void sendGMReset();
+
+ /**
* Transmit a sysEx to the midi device.
*
* The given msg MUST NOT contain the usual SysEx frame, i.e.
diff --git a/sound/mods/tfmx.cpp b/sound/mods/tfmx.cpp
index ad468033c5..6ed1abcfb5 100644
--- a/sound/mods/tfmx.cpp
+++ b/sound/mods/tfmx.cpp
@@ -36,17 +36,22 @@
// couple debug-functions
namespace {
- void displayPatternstep(const void *const vptr);
- void displayMacroStep(const void *const vptr);
- const uint16 noteIntervalls[64] = {
+#if 0
+void displayPatternstep(const void * const vptr);
+void displayMacroStep(const void * const vptr);
+#endif
+
+static const uint16 noteIntervalls[64] = {
1710, 1614, 1524, 1438, 1357, 1281, 1209, 1141, 1077, 1017, 960, 908,
856, 810, 764, 720, 680, 642, 606, 571, 539, 509, 480, 454,
428, 404, 381, 360, 340, 320, 303, 286, 270, 254, 240, 227,
214, 202, 191, 180, 170, 160, 151, 143, 135, 127, 120, 113,
214, 202, 191, 180, 170, 160, 151, 143, 135, 127, 120, 113,
- 214, 202, 191, 180 };
-}
+ 214, 202, 191, 180
+};
+
+} // End of anonymous namespace
namespace Audio {
@@ -101,7 +106,7 @@ void Tfmx::interrupt() {
// externally queued macros
if (channel.customMacro) {
- const byte *const noteCmd = (const byte *)&channel.customMacro;
+ const byte * const noteCmd = (const byte *)&channel.customMacro;
channel.sfxLocked = false;
noteCommand(noteCmd[0], noteCmd[1], (noteCmd[2] & 0xF0) | (uint8)i, noteCmd[3]);
channel.customMacro = 0;
@@ -276,7 +281,7 @@ void Tfmx::macroRun(ChannelContext &channel) {
continue;
case 0x04: // Wait. Parameters: Ticks to wait(W).
- // TODO: some unkown Parameter? (macroPtr[1] & 1)
+ // TODO: some unknown Parameter? (macroPtr[1] & 1)
channel.macroWait = READ_BE_UINT16(&macroPtr[2]);
break;
@@ -1096,10 +1101,11 @@ int Tfmx::doSfx(uint16 sfxIndex, bool unlockChannel) {
} // End of namespace Audio
// some debugging functions
+#if 0
namespace {
-#if !defined(NDEBUG) && 0
-void displayMacroStep(const void *const vptr) {
- const char *tableMacros[] = {
+
+void displayMacroStep(const void * const vptr) {
+ static const char *tableMacros[] = {
"DMAoff+Resetxx/xx/xx flag/addset/vol ",
"DMAon (start sample at selected begin) ",
"SetBegin xxxxxx sample-startadress",
@@ -1144,15 +1150,15 @@ void displayMacroStep(const void *const vptr) {
"SID stop xx.... flag (1=clear all)"
};
- const byte *const macroData = (const byte *const)vptr;
+ const byte *const macroData = (const byte * const)vptr;
if (macroData[0] < ARRAYSIZE(tableMacros))
debug("%s %02X%02X%02X", tableMacros[macroData[0]], macroData[1], macroData[2], macroData[3]);
else
- debug("Unkown Macro #%02X %02X%02X%02X", macroData[0], macroData[1], macroData[2], macroData[3]);
+ debug("Unknown Macro #%02X %02X%02X%02X", macroData[0], macroData[1], macroData[2], macroData[3]);
}
-void displayPatternstep(const void *const vptr) {
- const char *tablePatterns[] = {
+void displayPatternstep(const void * const vptr) {
+ static const char *tablePatterns[] = {
"End --Next track step--",
"Loop[count / step.w]",
"Cont[patternno./ step.w]",
@@ -1171,7 +1177,7 @@ void displayPatternstep(const void *const vptr) {
"NOP!-no operation-------"
};
- const byte *const patData = (const byte *const)vptr;
+ const byte * const patData = (const byte * const)vptr;
const byte command = patData[0];
if (command < 0xF0) { // Playnote
const byte flags = command >> 6; // 0-1 means note+detune, 2 means wait, 3 means portamento?
@@ -1180,10 +1186,8 @@ void displayPatternstep(const void *const vptr) {
} else
debug("%s %02X%02X%02X",tablePatterns[command & 0xF], patData[1], patData[2], patData[3]);
}
-#else
-void displayMacroStep(const void *const vptr, int chan, int index) {}
-void displayPatternstep(const void *const vptr) {}
+
+} // End of anonymous namespace
#endif
-} // End of namespace
#endif // #if defined(SOUND_MODS_TFMX_H)
diff --git a/sound/module.mk b/sound/module.mk
index 70bffb13d2..7992d3822b 100644
--- a/sound/module.mk
+++ b/sound/module.mk
@@ -33,6 +33,7 @@ MODULE_OBJS := \
mods/soundfx.o \
mods/tfmx.o \
softsynth/adlib.o \
+ softsynth/cms.o \
softsynth/opl/dbopl.o \
softsynth/opl/dosbox.o \
softsynth/opl/mame.o \
diff --git a/sound/softsynth/cms.cpp b/sound/softsynth/cms.cpp
new file mode 100644
index 0000000000..b307146f14
--- /dev/null
+++ b/sound/softsynth/cms.cpp
@@ -0,0 +1,376 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 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/softsynth/cms.h"
+#include "sound/null.h"
+
+#include "common/textconsole.h"
+#include "common/translation.h"
+#include "common/debug.h"
+
+// CMS/Gameblaster Emulation taken from DosBox
+
+#define LEFT 0x00
+#define RIGHT 0x01
+
+static const 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 const 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
+};
+
+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] = ::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 - ::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] = ::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:
+ // The CMS allows all registers to be written, so we just output some debug
+ // message here
+ debug(5, "CMS Unknown write to reg %x with %x",reg, data);
+ }
+}
+
+class CMSMusicPlugin : public NullMusicPlugin {
+public:
+ const char *getName() const {
+ return _s("Creative Music System Emulator");
+ }
+
+ const char *getId() const {
+ return "cms";
+ }
+
+ MusicDevices getDevices() const;
+};
+
+MusicDevices CMSMusicPlugin::getDevices() const {
+ MusicDevices devices;
+ devices.push_back(MusicDevice(this, "", MT_CMS));
+ return devices;
+}
+
+//#if PLUGIN_ENABLED_DYNAMIC(CMS)
+ //REGISTER_PLUGIN_DYNAMIC(CMS, PLUGIN_TYPE_MUSIC, CMSMusicPlugin);
+//#else
+ REGISTER_PLUGIN_STATIC(CMS, PLUGIN_TYPE_MUSIC, CMSMusicPlugin);
+//#endif
diff --git a/sound/softsynth/cms.h b/sound/softsynth/cms.h
new file mode 100644
index 0000000000..d5bb7f0a42
--- /dev/null
+++ b/sound/softsynth/cms.h
@@ -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$
+ */
+
+#ifndef SOUND_SOFTSYNTH_CMS_H
+#define SOUND_SOFTSYNTH_CMS_H
+
+#include "common/scummsys.h"
+
+/* 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 */
+};
+
+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);
+};
+
+
+#endif
diff --git a/sound/softsynth/fmtowns_pc98/towns_audio.cpp b/sound/softsynth/fmtowns_pc98/towns_audio.cpp
index 66a83f74ce..d745afbc17 100644
--- a/sound/softsynth/fmtowns_pc98/towns_audio.cpp
+++ b/sound/softsynth/fmtowns_pc98/towns_audio.cpp
@@ -226,10 +226,8 @@ TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfac
}
TownsAudioInterface::~TownsAudioInterface() {
- Common::StackLock lock(_mutex);
- reset();
- deinit();
_ready = false;
+ deinit();
delete[] _fmSaveReg[0];
delete[] _fmSaveReg[1];
@@ -243,9 +241,6 @@ bool TownsAudioInterface::init() {
if (_ready)
return true;
- if (!_drv)
- return false;
-
if (!TownsPC98_FmSynth::init())
return false;
@@ -260,9 +255,9 @@ bool TownsAudioInterface::init() {
setVolumeChannelMasks(-1, 0);
+ _ready = true;
callback(0);
- _ready = true;
return true;
}
@@ -359,8 +354,9 @@ void TownsAudioInterface::timerCallbackA() {
void TownsAudioInterface::timerCallbackB() {
Common::StackLock lock(_mutex);
- if (_drv && _ready) {
- _drv->timerCallback(1);
+ if (_ready) {
+ if (_drv)
+ _drv->timerCallback(1);
callback(80);
}
}
@@ -498,7 +494,7 @@ int TownsAudioInterface::intf_enableTimerB(va_list &args) {
int TownsAudioInterface::intf_loadSamples(va_list &args) {
uint32 dest = va_arg(args, uint32);
int size = va_arg(args, int);
- uint8 *src = va_arg(args, uint8*);
+ uint8 *src = va_arg(args, uint8*);
if (dest >= 65536 || size == 0 || size > 65536)
return 3;
@@ -570,7 +566,7 @@ int TownsAudioInterface::intf_loadWaveTable(va_list &args) {
TownsAudio_WaveTable *s = &_waveTables[_numWaveTables++];
s->readHeader(data);
-
+
_waveTablesTotalDataSize += s->size;
callback(32, _waveTablesTotalDataSize, s->size, data + 32);
@@ -665,7 +661,7 @@ int TownsAudioInterface::intf_pcmEffectPlaying(va_list &args) {
if (chan < 0x40 || chan > 0x47)
return 1;
chan -= 0x40;
- return (_pcmChanEffectPlaying & _chanFlags[chan]) ? true : false;
+ return (_pcmChanEffectPlaying & _chanFlags[chan]) ? 1 : 0;
}
int TownsAudioInterface::intf_fmKeyOn(va_list &args) {
@@ -726,11 +722,11 @@ int TownsAudioInterface::intf_setOutputVolume(va_list &args) {
static const uint8 flags[] = { 0x0C, 0x30, 0x40, 0x80 };
uint8 chan = (chanType & 0x40) ? 8 : 12;
-
+
chanType &= 3;
left = (left & 0x7e) >> 1;
right = (right & 0x7e) >> 1;
-
+
if (chan)
_outputVolumeFlags |= flags[chanType];
else
@@ -1402,13 +1398,13 @@ void TownsAudioInterface::updateOutputVolume() {
// FM Towns seems to support volumes of 0 - 63 for each channel.
// We recalculate sane values for our 0 to 255 volume range and
// balance values for our -128 to 127 volume range
-
+
// CD-AUDIO
uint32 maxVol = MAX(_outputLevel[12], _outputLevel[13]);
-
+
int volume = (int)(((float)(maxVol * 255) / 63.0f));
int balance = maxVol ? (int)( ( ((int)_outputLevel[13] - _outputLevel[12]) * 127) / (float)maxVol) : 0;
-
+
AudioCD.setVolume(volume);
AudioCD.setBalance(balance);
}
diff --git a/sound/softsynth/fmtowns_pc98/towns_euphony.cpp b/sound/softsynth/fmtowns_pc98/towns_euphony.cpp
index 0c0c203cc9..e6f94b29b5 100644
--- a/sound/softsynth/fmtowns_pc98/towns_euphony.cpp
+++ b/sound/softsynth/fmtowns_pc98/towns_euphony.cpp
@@ -36,17 +36,15 @@ TownsEuphonyDriver::TownsEuphonyDriver(Audio::Mixer *mixer) : _activeChannels(0)
}
TownsEuphonyDriver::~TownsEuphonyDriver() {
+ delete _intf;
delete[] _activeChannels;
delete[] _sustainChannels;
delete[] _assignedChannels;
-
delete[] _tEnable;
delete[] _tMode;
delete[] _tOrdr;
delete[] _tLevel;
delete[] _tTranspose;
-
- delete _intf;
}
bool TownsEuphonyDriver::init() {
@@ -131,7 +129,7 @@ void TownsEuphonyDriver::unloadWaveTable(int id) {
void TownsEuphonyDriver::reserveSoundEffectChannels(int num) {
_intf->callback(33, num);
uint32 volMask = 0;
-
+
if (num > 8)
return;
@@ -139,7 +137,7 @@ void TownsEuphonyDriver::reserveSoundEffectChannels(int num) {
volMask |= v;
v >>= 1;
}
-
+
_intf->setSoundEffectChanMask(volMask);
}
@@ -560,10 +558,10 @@ uint8 TownsEuphonyDriver::appendEvent(uint8 evt, uint8 chan) {
void TownsEuphonyDriver::sendEvent(uint8 mode, uint8 command) {
if (mode == 0) {
- warning("TownsEuphonyDriver: Mode 0 not implemented.");
+ // warning("TownsEuphonyDriver: Mode 0 not implemented");
} else if (mode == 0x10) {
- warning("TownsEuphonyDriver: Mode 0x10 not implemented.");
+ warning("TownsEuphonyDriver: Mode 0x10 not implemented");
} else if (mode == 0xff) {
if (command >= 0xf0) {
diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
index 7b7fbddc4b..303f08e6b1 100644
--- a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
+++ b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.cpp
@@ -160,6 +160,7 @@ public:
void reset();
};
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
class TownsPC98_MusicChannelPCM : public TownsPC98_MusicChannel {
public:
TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs,
@@ -178,6 +179,7 @@ private:
typedef bool (TownsPC98_MusicChannelPCM::*ControlEventFunc)(uint8 para);
const ControlEventFunc *controlEvents;
};
+#endif
TownsPC98_MusicChannel::TownsPC98_MusicChannel(TownsPC98_AudioDriver *driver, uint8 regOffs, uint8 flgs, uint8 num,
uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key),
@@ -312,7 +314,7 @@ void TownsPC98_MusicChannel::processEvents() {
void TownsPC98_MusicChannel::processFrequency() {
if (_flags & CHS_RECALCFREQ) {
- _frequency = (((const uint16 *)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f] + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8);
+ _frequency = (READ_LE_UINT16(&_drv->_opnFreqTable[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB) | (((_frqBlockMSB & 0x70) >> 1) << 8);
_drv->writeReg(_part, _regOffset + 0xa4, (_frequency >> 8));
_drv->writeReg(_part, _regOffset + 0xa0, (_frequency & 0xff));
@@ -709,7 +711,7 @@ void TownsPC98_MusicChannelSSG::processFrequency() {
if (_flags & CHS_RECALCFREQ) {
_block = _frqBlockMSB >> 4;
- _frequency = ((const uint16 *)_drv->_opnFreqTableSSG)[_frqBlockMSB & 0x0f] + _frqLSB;
+ _frequency = READ_LE_UINT16(&_drv->_opnFreqTableSSG[(_frqBlockMSB & 0x0f) << 1]) + _frqLSB;
uint16 f = _frequency >> _block;
_drv->writeReg(_part, _regOffset << 1, f & 0xff);
@@ -928,6 +930,7 @@ void TownsPC98_SfxChannel::reset() {
_drv->_ssgPatches[i + 12] = src[i + 12];
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_MusicChannelPCM::TownsPC98_MusicChannelPCM(TownsPC98_AudioDriver *driver, uint8 regOffs,
uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) :
TownsPC98_MusicChannel(driver, regOffs, flgs, num, key, prt, id), controlEvents(0) {
@@ -1016,9 +1019,13 @@ bool TownsPC98_MusicChannelPCM::control_ff_endOfTrack(uint8 para) {
return false;
}
}
+#endif // DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type) : TownsPC98_FmSynth(mixer, type),
- _channels(0), _ssgChannels(0), _sfxChannels(0), _rhythmChannel(0),
+ _channels(0), _ssgChannels(0), _sfxChannels(0),
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
+ _rhythmChannel(0),
+#endif
_trackPtr(0), _sfxData(0), _sfxOffs(0), _ssgPatches(0),
_patches(0), _sfxBuffer(0), _musicBuffer(0),
@@ -1027,7 +1034,13 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type)
_updateChannelsFlag(type == kType26 ? 0x07 : 0x3F), _finishedChannelsFlag(0),
_updateSSGFlag(type == kTypeTowns ? 0x00 : 0x07), _finishedSSGFlag(0),
- _updateRhythmFlag(type == kType86 ? 0x01 : 0x00), _finishedRhythmFlag(0),
+ _updateRhythmFlag(type == kType86 ?
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
+ 0x01
+#else
+ 0x00
+#endif
+ : 0x00), _finishedRhythmFlag(0),
_updateSfxFlag(0), _finishedSfxFlag(0),
_musicTickCounter(0),
@@ -1040,10 +1053,8 @@ TownsPC98_AudioDriver::TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type)
}
TownsPC98_AudioDriver::~TownsPC98_AudioDriver() {
- Common::StackLock lock(_mutex);
- reset();
- deinit();
_ready = false;
+ deinit();
if (_channels) {
for (int i = 0; i < _numChan; i++)
@@ -1062,8 +1073,9 @@ TownsPC98_AudioDriver::~TownsPC98_AudioDriver() {
delete _sfxChannels[i];
delete[] _sfxChannels;
}
-
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
delete _rhythmChannel;
+#endif
delete[] _ssgPatches;
}
@@ -1107,10 +1119,12 @@ bool TownsPC98_AudioDriver::init() {
}
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_hasPercussion) {
_rhythmChannel = new TownsPC98_MusicChannelPCM(this, 0, 0, 0, 0, 0, 1);
_rhythmChannel->init();
}
+#endif
setMusicTempo(84);
setSfxTempo(654);
@@ -1152,7 +1166,9 @@ void TownsPC98_AudioDriver::loadMusicData(uint8 *data, bool loadPaused) {
}
if (_hasPercussion) {
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
_rhythmChannel->loadData(data + READ_LE_UINT16(src_a));
+#endif
src_a += 2;
}
@@ -1212,8 +1228,10 @@ void TownsPC98_AudioDriver::reset() {
memcpy(_ssgPatches, _drvTables + 156, 256);
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_rhythmChannel)
_rhythmChannel->reset();
+#endif
}
void TownsPC98_AudioDriver::fadeStep() {
@@ -1233,10 +1251,12 @@ void TownsPC98_AudioDriver::fadeStep() {
if (!_fading) {
_fading = 19;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_hasPercussion) {
if (_updateRhythmFlag & _rhythmChannel->_idFlag)
_rhythmChannel->reset();
}
+#endif
} else {
if (!--_fading)
reset();
@@ -1263,9 +1283,11 @@ void TownsPC98_AudioDriver::timerCallbackB() {
}
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_hasPercussion)
if (_updateRhythmFlag & _rhythmChannel->_idFlag)
_rhythmChannel->processEvents();
+#endif
}
toggleRegProtection(false);
diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h
index 18daee1e72..00fcf7c5d5 100644
--- a/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h
+++ b/sound/softsynth/fmtowns_pc98/towns_pc98_driver.h
@@ -31,13 +31,17 @@
class TownsPC98_MusicChannel;
class TownsPC98_MusicChannelSSG;
class TownsPC98_SfxChannel;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
class TownsPC98_MusicChannelPCM;
+#endif
class TownsPC98_AudioDriver : public TownsPC98_FmSynth {
friend class TownsPC98_MusicChannel;
friend class TownsPC98_MusicChannelSSG;
friend class TownsPC98_SfxChannel;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
friend class TownsPC98_MusicChannelPCM;
+#endif
public:
TownsPC98_AudioDriver(Audio::Mixer *mixer, EmuType type);
~TownsPC98_AudioDriver();
@@ -84,7 +88,9 @@ protected:
TownsPC98_MusicChannel **_channels;
TownsPC98_MusicChannelSSG **_ssgChannels;
TownsPC98_SfxChannel **_sfxChannels;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_MusicChannelPCM *_rhythmChannel;
+#endif
const uint8 *_opnCarrier;
const uint8 *_opnFreqTable;
diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
index b51f695087..62f7d39771 100644
--- a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
+++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
@@ -377,6 +377,7 @@ private:
bool _ready;
};
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
class TownsPC98_FmSynthPercussionSource {
public:
TownsPC98_FmSynthPercussionSource(const uint32 timerbase, const uint32 rtt);
@@ -442,6 +443,7 @@ private:
bool _ready;
};
+#endif // DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_FmSynthSquareSineSource::TownsPC98_FmSynthSquareSineSource(const uint32 timerbase, const uint32 rtt) : _tlTable(0),
_rtt(rtt), _tleTable(0), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0), _reg(0), _rand(1), _outN(1),
@@ -627,6 +629,7 @@ void TownsPC98_FmSynthSquareSineSource::updateRegs() {
_updateRequest = -1;
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_FmSynthPercussionSource::TownsPC98_FmSynthPercussionSource(const uint32 timerbase, const uint32 rtt) :
_rtt(rtt), _tickLength(timerbase * 2), _timer(0), _ready(false), _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) {
@@ -823,11 +826,16 @@ void TownsPC98_FmSynthPercussionSource::advanceInput(RhtChannel *ins) {
cur >>= 4;
}
}
+#endif // DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) :
_mixer(mixer),
- _chanInternal(0), _ssg(0), _prc(0),
- _numChan(type == kType26 ? 3 : 6), _numSSG(type == kTypeTowns ? 0 : 3), _hasPercussion(type == kType86 ? true : false),
+ _chanInternal(0), _ssg(0),
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
+ _prc(0),
+#endif
+ _numChan(type == kType26 ? 3 : 6), _numSSG(type == kTypeTowns ? 0 : 3),
+ _hasPercussion(type == kType86 ? true : false),
_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0),
_rtt(type == kTypeTowns ? 0x514767 : 0x5B8D80), _baserate(55125.0f / (float)mixer->getOutputRate()),
_volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255),
@@ -836,9 +844,8 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) :
memset(&_timers[0], 0, sizeof(ChipTimer));
memset(&_timers[1], 0, sizeof(ChipTimer));
- _timers[0].cb = &TownsPC98_FmSynth::timerCallbackA;
- _timers[1].cb = &TownsPC98_FmSynth::timerCallbackB;
- _timerbase = (uint32)(_baserate * 1000000.0f);
+ _timers[0].cb = _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback;
+ _timerbase = (uint32)(_baserate * 1000000.0f);
}
TownsPC98_FmSynth::~TownsPC98_FmSynth() {
@@ -846,7 +853,9 @@ TownsPC98_FmSynth::~TownsPC98_FmSynth() {
deinit();
delete _ssg;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
delete _prc;
+#endif
delete[] _chanInternal;
delete[] _oprRates;
@@ -878,10 +887,12 @@ bool TownsPC98_FmSynth::init() {
_ssg->init(&_ssgTables[0], &_ssgTables[16]);
}
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_hasPercussion) {
_prc = new TownsPC98_FmSynthPercussionSource(_timerbase, _rtt);
_prc->init(_percussionData);
}
+#endif
_timers[0].cb = &TownsPC98_FmSynth::timerCallbackA;
_timers[1].cb = &TownsPC98_FmSynth::timerCallbackB;
@@ -895,6 +906,7 @@ bool TownsPC98_FmSynth::init() {
}
void TownsPC98_FmSynth::reset() {
+ Common::StackLock lock(_mutex);
for (int i = 0; i < _numChan; i++) {
for (int ii = 0; ii < 4; ii++)
_chanInternal[i].opr[ii]->reset();
@@ -910,8 +922,10 @@ void TownsPC98_FmSynth::reset() {
if (_ssg)
_ssg->reset();
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_prc)
_prc->reset();
+#endif
}
void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
@@ -945,9 +959,11 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
_ssg->writeReg(l, value);
break;
case 0x10:
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
// pcm rhythm channel
if (_prc)
_prc->writeReg(l, value);
+#endif
break;
case 0x20:
if (l == 8) {
@@ -1108,7 +1124,7 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
memset(tmp, 0, sizeof(int32) * numSamples);
int32 samplesLeft = numSamples >> 1;
- while (samplesLeft) {
+ while (_ready && samplesLeft) {
int32 render = samplesLeft;
for (int i = 0; i < 2; i++) {
@@ -1139,8 +1155,10 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
if (_ssg)
_ssg->nextTick(tmp, render);
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_prc)
_prc->nextTick(tmp, render);
+#endif
nextTickEx(tmp, render);
@@ -1160,10 +1178,9 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
}
void TownsPC98_FmSynth::deinit() {
- _mixer->stopHandle(_soundHandle);
- _timers[0].cb = &TownsPC98_FmSynth::idleTimerCallback;
- _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback;
_ready = false;
+ _mixer->stopHandle(_soundHandle);
+ _timers[0].cb = _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback;
}
uint8 TownsPC98_FmSynth::readSSGStatus() {
@@ -1176,8 +1193,10 @@ void TownsPC98_FmSynth::setVolumeIntern(int volA, int volB) {
_volumeB = CLIP<uint16>(volB, 0, Audio::Mixer::kMaxMixerVolume);
if (_ssg)
_ssg->setVolumeIntern(_volumeA, _volumeB);
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_prc)
_prc->setVolumeIntern(_volumeA, _volumeB);
+#endif
}
void TownsPC98_FmSynth::setVolumeChannelMasks(int channelMaskA, int channelMaskB) {
@@ -1186,8 +1205,10 @@ void TownsPC98_FmSynth::setVolumeChannelMasks(int channelMaskA, int channelMaskB
_volMaskB = channelMaskB;
if (_ssg)
_ssg->setVolumeChannelMasks(_volMaskA >> _numChan, _volMaskB >> _numChan);
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
if (_prc)
_prc->setVolumeChannelMasks(_volMaskA >> (_numChan + _numSSG), _volMaskB >> (_numChan + _numSSG));
+#endif
}
void TownsPC98_FmSynth::generateTables() {
@@ -1400,6 +1421,7 @@ const int TownsPC98_FmSynth::_ssgTables[] = {
0x000575, 0x000463, 0x00039D, 0x0002FA, 0x000242, 0x0001B6, 0x00014C, 0x0000FB
};
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
const uint8 TownsPC98_FmSynth::_percussionData[] = {
0, 24, 1, 192, 1, 216, 2, 128, 4, 88, 23, 64, 27, 152, 1, 128, 29, 24, 2, 128, 31, 152, 0, 128, 136, 128, 128, 128, 0, 136, 97, 103, 153, 139, 34, 163, 72, 195, 27, 69, 1, 154, 137, 35, 8, 51, 169, 122, 164, 75, 133, 203, 81, 146, 168, 121, 185, 68, 202, 8, 33, 237, 49, 177, 12, 133, 140, 17, 160, 42, 161, 10, 0, 137, 176, 57,
233, 41, 160, 136, 235, 65, 177, 137, 128, 26, 164, 28, 3, 157, 51, 137, 1, 152, 113, 161, 40, 146, 115, 192, 56, 5, 169, 66, 161, 56, 1, 50, 145, 59, 39, 168, 97, 1, 160, 57, 7, 153, 50, 153, 32, 2, 25, 129, 32, 20, 186, 66, 129, 24, 153, 164, 142, 130, 169, 153, 26, 242, 138, 217, 9, 128, 204, 58, 209, 172, 40, 176, 141,
@@ -1514,6 +1536,7 @@ const uint8 TownsPC98_FmSynth::_percussionData[] = {
45, 136, 18, 144, 105, 138, 1, 160, 14, 128, 132, 145, 186, 37, 138, 41, 192, 48, 145, 46, 160, 33, 44, 24, 225, 16, 13, 132, 136, 137, 16, 148, 25, 170, 194, 82, 152, 136, 91, 24, 42, 169, 33, 233, 131, 179, 24, 185, 149, 16, 57, 172, 164, 18, 10, 211, 160, 147, 211, 33, 138, 243, 129, 16, 41, 193, 0, 43, 132, 155, 73,
58, 145, 244, 145, 43, 35, 9, 171, 16, 110, 25, 8, 28, 74, 162, 128, 26, 27, 82, 45, 136, 153, 18, 8, 136, 8
};
+#endif // DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_FmSynth::ChanInternal::ChanInternal() {
memset(this, 0, sizeof(ChanInternal));
diff --git a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
index 4a618338ed..ddd249b1b8 100644
--- a/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
+++ b/sound/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
@@ -30,9 +30,21 @@
#include "sound/mixer.h"
#include "common/list.h"
+#ifdef __DS__
+/* This disables the rhythm channel when emulating the PC-98 type 86 sound card.
+ * The only purpose is code size reduction for certain backends.
+ * At the moment the only games which make use of the rhythm channel are the
+ * (very rare) PC-98 versions of Legend of Kyrandia 2 and Lands of Lore. Music will
+ * still be okay, just missing a couple of rhythm instruments.
+ */
+#define DISABLE_PC98_RHYTHM_CHANNEL
+#endif
+
class TownsPC98_FmSynthOperator;
class TownsPC98_FmSynthSquareSineSource;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
class TownsPC98_FmSynthPercussionSource;
+#endif
enum EnvelopeState {
kEnvReady,
@@ -128,7 +140,9 @@ private:
};
TownsPC98_FmSynthSquareSineSource *_ssg;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
TownsPC98_FmSynthPercussionSource *_prc;
+#endif
ChanInternal *_chanInternal;
uint8 *_oprRates;
@@ -168,7 +182,9 @@ private:
Audio::Mixer *_mixer;
Audio::SoundHandle _soundHandle;
+#ifndef DISABLE_PC98_RHYTHM_CHANNEL
static const uint8 _percussionData[];
+#endif
static const uint32 _adtStat[];
static const uint8 _detSrc[];
static const int _ssgTables[];
diff --git a/sound/softsynth/mt32/mt32_file.cpp b/sound/softsynth/mt32/mt32_file.cpp
index f4eba73d33..ce5c2874c4 100644
--- a/sound/softsynth/mt32/mt32_file.cpp
+++ b/sound/softsynth/mt32/mt32_file.cpp
@@ -19,6 +19,12 @@
* IN THE SOFTWARE.
*/
+
+// FIXME: Disable symbol overrides so that we can use system headers.
+// But we *really* should get rid of this usage of FILE, fopen etc.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+
#include <stdio.h>
#include "mt32emu.h"
@@ -37,15 +43,15 @@ namespace MT32Emu {
}
void ANSIFile::close() {
- fclose(fp);
+ fclose((FILE *)fp);
}
size_t ANSIFile::read(void *in, size_t size) {
- return fread(in, 1, size, fp);
+ return fread(in, 1, size, (FILE *)fp);
}
bool ANSIFile::readBit8u(Bit8u *in) {
- int c = fgetc(fp);
+ int c = fgetc((FILE *)fp);
if (c == EOF)
return false;
*in = (Bit8u)c;
@@ -69,11 +75,11 @@ namespace MT32Emu {
}
size_t ANSIFile::write(const void *out, size_t size) {
- return fwrite(out, 1, size, fp);
+ return fwrite(out, 1, size, (FILE *)fp);
}
bool ANSIFile::writeBit8u(Bit8u out) {
- return fputc(out, fp) != EOF;
+ return fputc(out, (FILE *)fp) != EOF;
}
bool File::writeBit16u(Bit16u out) {
@@ -103,6 +109,6 @@ namespace MT32Emu {
}
bool ANSIFile::isEOF() {
- return feof(fp) != 0;
+ return feof((FILE *)fp) != 0;
}
}
diff --git a/sound/softsynth/mt32/mt32_file.h b/sound/softsynth/mt32/mt32_file.h
index 27c8ccbe46..b311ad9626 100644
--- a/sound/softsynth/mt32/mt32_file.h
+++ b/sound/softsynth/mt32/mt32_file.h
@@ -49,7 +49,7 @@ public:
class ANSIFile: public File {
private:
- FILE *fp;
+ void *fp;
public:
bool open(const char *filename, OpenMode mode);
void close();
diff --git a/sound/softsynth/mt32/tables.cpp b/sound/softsynth/mt32/tables.cpp
index b0414154dc..722a5e3c41 100644
--- a/sound/softsynth/mt32/tables.cpp
+++ b/sound/softsynth/mt32/tables.cpp
@@ -587,7 +587,7 @@ File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float r
initSaw(noteLookup, noteLookup->div2);
//synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
- file = initWave(synth, noteLookup, (const float)WGAMP, div2, file);
+ file = initWave(synth, noteLookup, WGAMP, div2, file);
// Create the pitch tables
if (noteLookup->wavTable == NULL)
diff --git a/sound/softsynth/opl/mame.cpp b/sound/softsynth/opl/mame.cpp
index f6da659918..c875080e8f 100644
--- a/sound/softsynth/opl/mame.cpp
+++ b/sound/softsynth/opl/mame.cpp
@@ -694,7 +694,7 @@ static int OPLOpenTable(void) {
return 0;
}
/* make total level table */
- for (t = 0; t < EG_ENT - 1 ; t++) {
+ for (t = 0; t < EG_ENT - 1; t++) {
rate = ((1 << TL_BITS) - 1) / pow(10.0, EG_STEP * t / 20); /* dB -> voltage */
TL_TABLE[ t] = (int)rate;
TL_TABLE[TL_MAX + t] = -TL_TABLE[t];
@@ -1082,10 +1082,10 @@ void OPLResetChip(FM_OPL *OPL) {
for (i = 0xff; i >= 0x20; i--)
OPLWriteReg(OPL,i,0);
/* reset OPerator parameter */
- for (c = 0; c < OPL->max_ch ;c++ ) {
+ for (c = 0; c < OPL->max_ch; c++) {
OPL_CH *CH = &OPL->P_CH[c];
/* OPL->P_CH[c].PAN = OPN_CENTER; */
- for (s = 0; s < 2; s++ ) {
+ for (s = 0; s < 2; s++) {
/* wave table */
CH->SLOT[s].wavetable = &SIN_TABLE[0];
/* CH->SLOT[s].evm = ENV_MOD_RR; */
diff --git a/test/common/array.h b/test/common/array.h
index c85e056b19..f17edd3984 100644
--- a/test/common/array.h
+++ b/test/common/array.h
@@ -78,6 +78,36 @@ class ArrayTestSuite : public CxxTest::TestSuite
TS_ASSERT_EQUALS(array.size(), (unsigned int)5);
}
+ void test_insert_at_array() {
+ Common::Array<int> array;
+ Common::Array<int> array2;
+
+ // First of all some data
+ array.push_back(-12);
+ array.push_back(17);
+ array.push_back(25);
+ array.push_back(-11);
+
+ array2.push_back(42);
+ array2.push_back(105);
+ array2.push_back(-1);
+
+ // Insert some data
+ array.insert_at(2, array2);
+
+ TS_ASSERT_EQUALS(array.size(), (unsigned int)7);
+
+ TS_ASSERT_EQUALS(array[0], -12);
+ TS_ASSERT_EQUALS(array[1], 17);
+ TS_ASSERT_EQUALS(array[2], 42);
+ TS_ASSERT_EQUALS(array[3], 105);
+ TS_ASSERT_EQUALS(array[4], -1);
+ TS_ASSERT_EQUALS(array[5], 25);
+ TS_ASSERT_EQUALS(array[6], -11);
+
+ }
+
+
void test_remove_at() {
Common::Array<int> array;
diff --git a/test/common/rational.h b/test/common/rational.h
index 4248283ade..21b84a8a41 100644
--- a/test/common/rational.h
+++ b/test/common/rational.h
@@ -17,9 +17,18 @@ public:
Common::Rational r6 = r5 - 1;
TS_ASSERT(r4 == r5);
+ TS_ASSERT(!(r4 != r5));
+
+ TS_ASSERT(r4 != r6);
+ TS_ASSERT(!(r4 == r6));
TS_ASSERT(-r4 == -r5);
+ TS_ASSERT(r0 == 2);
+ TS_ASSERT(!(r0 != 2));
+ TS_ASSERT(!(r3 == 2));
+ TS_ASSERT(r3 != 2);
+
TS_ASSERT( r4 > r6);
TS_ASSERT( r4 >= r6);
TS_ASSERT(!(r4 < r6));
@@ -70,6 +79,11 @@ public:
TS_ASSERT_EQUALS(r1 + r0, Common::Rational(5, 2));
TS_ASSERT_EQUALS(r0 - r1, Common::Rational(3, 2));
TS_ASSERT_EQUALS(r1 - r0, Common::Rational(-3, 2));
+
+ TS_ASSERT_EQUALS(1 + r1, Common::Rational(3, 2));
+ TS_ASSERT_EQUALS(r1 + 1, Common::Rational(3, 2));
+ TS_ASSERT_EQUALS(1 - r1, Common::Rational(1, 2));
+ TS_ASSERT_EQUALS(r1 - 1, Common::Rational(-1, 2));
}
void test_mul() {
@@ -86,6 +100,9 @@ public:
TS_ASSERT_EQUALS((-r2) * r3, -r4);
TS_ASSERT_EQUALS(r2 * (-r3), -r4);
TS_ASSERT_EQUALS((-r2) * (-r3), r4);
+
+ TS_ASSERT_EQUALS(r1 * 2, 1);
+ TS_ASSERT_EQUALS(2 * r1, 1);
}
void test_div() {
@@ -93,5 +110,8 @@ public:
Common::Rational r1(1, 2);
TS_ASSERT_EQUALS(r0 / r1, 4);
+
+ TS_ASSERT_EQUALS(r1 / 2, Common::Rational(1, 4));
+ TS_ASSERT_EQUALS(2 / r1, Common::Rational(4, 1));
}
};
diff --git a/test/common/str.h b/test/common/str.h
index e1f2b39578..0908b21c1e 100644
--- a/test/common/str.h
+++ b/test/common/str.h
@@ -310,17 +310,20 @@ class StringTestSuite : public CxxTest::TestSuite
TS_ASSERT(Common::matchString("monkey.s01", "monkey.s*1"));
TS_ASSERT(!Common::matchString("monkey.s99", "monkey.s*1"));
TS_ASSERT(Common::matchString("monkey.s101", "monkey.s*1"));
+
+ TS_ASSERT(!Common::String("").matchString("*_"));
+ TS_ASSERT(Common::String("a").matchString("a***"));
}
void test_string_printf() {
- TS_ASSERT( Common::String::printf("") == "" );
- TS_ASSERT( Common::String::printf("%s", "test") == "test" );
- TS_ASSERT( Common::String::printf("%s.s%.02d", "monkey", 1) == "monkey.s01" );
- TS_ASSERT( Common::String::printf("Some %s to make this string longer than the default built-in %s %d", "text", "capacity", 123456) == "Some text to make this string longer than the default built-in capacity 123456" );
+ TS_ASSERT_EQUALS( Common::String::printf(""), "" );
+ TS_ASSERT_EQUALS( Common::String::printf("%s", "test"), "test" );
+ TS_ASSERT_EQUALS( Common::String::printf("%s.s%.02d", "monkey", 1), "monkey.s01" );
+ TS_ASSERT_EQUALS( Common::String::printf("Some %s to make this string longer than the default built-in %s %d", "text", "capacity", 123456), "Some text to make this string longer than the default built-in capacity 123456" );
Common::String s = Common::String::printf("%s%X", "test", 1234);
- TS_ASSERT(s == "test4D2");
- TS_ASSERT(s.size() == 7);
+ TS_ASSERT_EQUALS(s, "test4D2");
+ TS_ASSERT_EQUALS(s.size(), 7U);
}
void test_strlcpy() {
diff --git a/tools/README b/tools/README
index 6ccd7b3694..0a9b4efa60 100644
--- a/tools/README
+++ b/tools/README
@@ -6,28 +6,28 @@ been warned :-).
agi-palex.py (buddha)
------------
- Tool for extracting palettes from Amiga AGI games' executables.
+ Tool for extracting palettes from Amiga AGI games' executables.
construct-pred-dict.pl, extract-words-tok.pl (sev)
--------------------------------------------
- Tools related to predictive input for AGI engine.
+ Tools related to predictive input for AGI engine.
convbdf
-------
- Tool which converts BDF fonts (BDF = Bitmap Distribution Format) to
- C++ source. That source, after being slightly tweaked, can be used to
- replace or add fonts for the ScummVM GUI.
+ Tool which converts BDF fonts (BDF = Bitmap Distribution Format) to
+ C++ source. That source, after being slightly tweaked, can be used to
+ replace or add fonts for the ScummVM GUI.
- There is also a ttf2bdf tool which allows you to convert TrueType
- fonts to BDF.
+ There is also a ttf2bdf tool which allows you to convert TrueType
+ fonts to BDF.
- Hint from SumthinWicked: If you use ttf2bdf, it'll convert all glyphs
- to bitmaps, but ScummVM only needs some of them. So you may want to
- do your conversion like this:
- ttf2bdf -p SIZE -l "32_160" -o FONT.bdf FONT.ttf
- where SIZE is replaced by the desired font height.
+ Hint from SumthinWicked: If you use ttf2bdf, it'll convert all glyphs
+ to bitmaps, but ScummVM only needs some of them. So you may want to
+ do your conversion like this:
+ ttf2bdf -p SIZE -l "32_160" -o FONT.bdf FONT.ttf
+ where SIZE is replaced by the desired font height.
create_drascula (sev)
@@ -39,11 +39,13 @@ create_drascula (sev)
(mostly the dialog subtitles) in English, Spanish, German, French
and Italian. This tool is used to create the drascula.dat file.
+
create_hugo (Strangerke)
-----------
- Creates hugo.dat file which contains all kinds of static data contained
+ Creates hugo.dat file which contains all kinds of static data contained
in original game executable.
+
create_kyradat (LordHoto, athrxx)
--------------
Extracts various static data from the original game executables.
@@ -70,6 +72,13 @@ create_msvc (LordHoto, Littleboy (contributor))
for further help.
+create_toon (Strangerke)
+-----------
+ This tool creates toon.dat, which contains all the game's texts
+ hardcoded in original game executable. This includes English, French,
+ German, Russian and Spanish texts.
+
+
create_translations (criezy)
-------------------
Creates the translations.dat file from po files given as arguments.
@@ -78,10 +87,10 @@ create_translations (criezy)
credits.pl
----------
- This perl script contains credits to the many people who helped with
- ScummVM, and it is used to create the credits lists that occur in
- various places, including the AUTHORS file, the about dialog, and our
- web site.
+ This perl script contains credits to the many people who helped with
+ ScummVM, and it is used to create the credits lists that occur in
+ various places, including the AUTHORS file, the about dialog, and our
+ web site.
dist-scummvm.sh
@@ -110,9 +119,9 @@ dist-scummvm.sh
make-scumm-fontdata (eriktorbjorn)
-------------------
- Tool that generates compressed font data used in SCUMM: To get rid of
- a few kilobytes of hard-coded font data, we only store how the
- French, German, Italian and Spanish fonts differ from the English one.
+ Tool that generates compressed font data used in SCUMM: To get rid of
+ a few kilobytes of hard-coded font data, we only store how the
+ French, German, Italian and Spanish fonts differ from the English one.
md5table
@@ -123,9 +132,9 @@ md5table
qtable (cyx)
-------
- This tool generates the "queen.tbl" file.
+ This tool generates the "queen.tbl" file.
skycpt (lavosspawn)
-------
- This tool generates the "SKY.CPT" file.
+ This tool generates the "SKY.CPT" file.
diff --git a/tools/create_drascula/staticdata.h b/tools/create_drascula/staticdata.h
index 198cc66790..51ed995884 100644
--- a/tools/create_drascula/staticdata.h
+++ b/tools/create_drascula/staticdata.h
@@ -2700,46 +2700,46 @@ const char *_text[NUM_LANGS][NUM_TEXT] = {
{
// 0
"",
- "C'EST LA DEUXI\212ME PORTE PLUS GRANDE QUE J'AI VUE DANS MA VIE.",
- "ENFIN, PAS AUTANT QUE \207A.",
- "ELLE EST BOUCH\202E AVEC DES GROSSES PLANCHES. L'\202GLISE EST PEUT-\210TRE ABANDONN\202E DEPUIS QUELQUES ANN\202ES.",
+ "C'EST LA DEUXI\212ME PORTE LA PLUS GRANDE QUE J'AI VUE DANS MA VIE.",
+ "ENFIN, PAS TANT QUE \207A.",
+ "ELLE EST OBTUR\202E AVEC DES GROSSES PLANCHES. L'\202GLISE DOIT \210TRE ABANDONN\202E DEPUIS PLUSIEURS ANN\202ES.",
"MAIS JE NE L'AI PAS OUVERTE.",
// 5
- "QU'EST-CE QUE JE FAIS? JE L'ARRACHE?",
+ "QUE DOIS-JE FAIRE? JE L'ARRACHE?",
"BONJOUR, PORTE. JE VAIS T'ENCADRER.",
- "C'EST TROP POUR MOI.",
- "UNE FEN\210TRE BOUCH\202E AUX GROSSES PLANCHES.",
- "JE N'ARRIVE PAS.",
+ "C'EST TROP DIFFICILE POUR MOI.",
+ "LA FEN\210TRE EST BLOQU\202E AVEC DES GROSSES PLANCHES.",
+ "JE N'Y ARRIVE PAS.",
// 10
- "\200A Y EST.",
+ "\200A Y EST, C'EST FAIT.",
"ET POURQUOI?",
- "SALUT, FEN\210TRE! AS-TU QUELQUE CHOSE \205 FAIRE CE SOIR?",
- "PAS SANS LE PERMIS DE TRAVAUX PUBLIQUES.",
- "H\202! CETTE FEN\210TRE A SEULEMENT UNE GROSSE PLANCHE...",
+ "SALUT, FEN\210TRE! AS-TU QUELQUE CHOSE DE PR\202VU CE SOIR?",
+ "PAS SANS UN PERMIS DES TRAVAUX PUBLIQUES.",
+ "SI SEULEMENT CETTE FEN\210TRE N'\202TAIT PAS BLOQU\202E...",
// 15
- "OH\202! OH\202!-FEN\210TRE!",
+ "\202HO! FEN\210TRE!",
"BONJOUR, TOI.",
- "",
- "JE N'ARRIVE PAS.",
- "C'EST BIEN O\227 ELLE EST.",
+ "COMME MICROCHOF",
+ "JE NE PEUX PAS L'ATTEINDRE.",
+ "C'EST TR\324S BIEN O\227 C'EST.",
// 20
"",
"C'EST UNE TOMBE EN FORME DE CROIX.",
"NON, MERCI.",
- "BONJOUR, LE D\202FUNT: VEUX-TU DES VERMISSEAUX?",
- "MAIS OUI. COMME EN POLTERGUEIST.",
+ "BONJOUR, LE MORT: VEUX-TU DES VERMISSEAUX?",
+ "MAIS OUI. COMME DANS POLTERGUEIST.",
// 25
"",
"",
- "JE REVIENS EN QUINZE MINUTES.",
+ "JE REVIENS DANS QUINZE MINUTES.",
"D\202FENSE D'AFFICHER.",
- "",
+ "C'EST LA TOMBE D'ONCLE EVARISTO!",
// 30
"C'EST FERM\202 \205 CL\202.",
"J'EN AI D\202J\205 UN.",
- "",
- "IL NE R\202POND PAS.",
- "MAIS NON, C'EST BIEN GAR\202.",
+ "\202HO, ONCLE EVARISTO!",
+ "IL NE SE PASSE RIEN",
+ "C'EST MAL GAR\202.",
// 35
"C'EST UNE PORTE.",
"UN TIROIR DE LA TABLE.",
@@ -2749,7 +2749,7 @@ const char *_text[NUM_LANGS][NUM_TEXT] = {
// 40
"",
"C'EST UN CAND\202LABRE TR\212S VIEUX.",
- "IL DOIT \210TRE L\205 D\212S QUE MAZINGUER-Z \202TAIT UNE VIS.",
+ "IL DOIT \210TRE L\205 DEPUIS QUE MAZINGUER-Z PASSAIT \205 LA T\202L\202.",
"NON, C'EST UNE RELIQUE.",
"C'EST UN JOLI R\202TABLE.",
// 45
@@ -2759,7 +2759,7 @@ const char *_text[NUM_LANGS][NUM_TEXT] = {
"NON.",
"",
// 50
- "HA! HA! HA! -QUE C'EST BON!",
+ "HA! HA! HA! QUE C'EST BON!",
"",
"",
"",
@@ -2767,9 +2767,9 @@ const char *_text[NUM_LANGS][NUM_TEXT] = {
// 55
"C'EST FERNAN, LA PLANTE.",
"C'EST UNE DES PIQUES DE LA GRILLE.",
- "H\202! L\205-DESSOUS IL Y A UNE BO\214TE D'ALLUMETTES.",
- "REGARDE! UN PAQUET DE CLINEX. -ET IL Y A UN TOUT NEUF!",
- "IL N'Y A RIEN DE PLUS DANS LE SEAU.",
+ "H\202! IL Y A UNE BO\214TE D'ALLUMETTES L\205-DESSOUS .",
+ "REGARDE! UN PAQUET DE CLINEX. ET IL Y EN A UN INUTILIS\202!",
+ "IL N'Y A RIEN DE PLUS DANS LA POUBELLE.",
// 60
"C'EST UN AVEUGLE QUI VE VOIT PAS.",
"",
@@ -2819,487 +2819,487 @@ const char *_text[NUM_LANGS][NUM_TEXT] = {
"",
"",
// 100
- "ELLE N'A RIEN DE SP\220CIAL",
+ "RIEN DE SP\220CIAL",
"CELA N'A RIEN D'EXTRAORDINAIRE",
- "QU'EST-CE QU'IL Y A?",
- "BONJOUR!",
+ "H\202, QUOI DE NEUF MEC?",
+ "SALUT!",
"RIEN DE NOUVEAU?",
// 105
- "LA FAMILLE, \200A VA?",
- "-QUELLES CHOSES TU AS!",
- "MAIS, COMME JE VAIS PRENDRE CELA!",
+ "LA FAMILLE, \207A VA?",
+ "C'EST BIEN TOI \207A!",
+ "MAIS, COMME JE VAIS PRENDRE \207A!",
"MA RELIGION ME L'INTERDIT",
- "CE N'EST PAS MIEUX",
+ "CELA NE VAUT MIEUX PAS",
// 110
- "BIEN S\352R, MON VIEUX!",
- "ON NE PARLE PLUS DE CELA",
+ "BIEN S\352R, MEC!",
+ "PAS QUESTION",
"IMPOSSIBLE",
"CELA NE S'OUVRE PAS",
- "JE NE PEUX PAS TOUT SEUL",
+ "JE NE PEUX PAS LE FAIRE TOUT SEUL",
// 115
- "SI JE VOULAIS, J' ARRIVERAIS, MAIS \200A ME DONNE DE LA PARESSE",
- "JE N'Y VOIT PAS UNE RAISON APPARENTE",
- "C'EST UN CERVEAU ASSEZ BIEN",
- "ET BIEN CERVEAU, QUE PENSES-TU FAIRE CE SOIR?",
- "NON, ON DOIT LE GARDER DANS UN ENDROIT \267 L'ABRI DES MUTATIONS DE L'ATMOSPH\324RE",
+ "JE POURRAIS LE FAIRE, MAIS JE ME SENS UN PEU PARESSEUX",
+ "JE N'EN VOIS PAS LA RAISON",
+ "C'EST UN BEAU CERVEAU",
+ "ET ALORS CERVEAU, QUE FAIS-TU CE SOIR?",
+ "NON, ON DOIT LE GARDER DANS UN ENDROIT \267 L'ABRI DES EFFETS MUTAG\324NE DE L'ATMOSPH\324RE",
// 120
"C'EST UN DUR, COMME MON CHEF",
- "C'EST UN PIEU TR\324S AIGUIS\220",
- "FID\324LE PIEU POINTUUU, NOBLE CH\322NE TRANSYLVAAAN",
+ "UN PIEU TR\324S AIGUIS\220",
+ "TOI FID\324LE PIEU POINTU, FAIT AVEC LE BOIS DU PLUS NOBLE CH\322NE TRANSYLVANIEN",
"TIENS! JE DOIS COUPER MES ONGLES!",
"B.J. EST L\267-DEDANS. ET QUELLE EST MIGNONE CETTE NANA!",
// 125
"ELLE EST FERM\220E TR\324S SOLIDEMENT",
"\"CADENAS SOLIDES S.A.\"",
- "C'EST LE TYPIQUE SQUELETTE QU'ON TROUVE DANS LES GE\342LES DE TOUS LES JEUX",
- "ON L' EMPLOIE NORMALEMENT POUR INDUIR DU COURANT \220LECTRIQUE AUX APPAREILS QU'Y SONT RACCORD\220S",
- "C'EST ABSOLUMENT ARTISANAL, CAR LES JAPONAIS LES FONT MAINTENANT DE POCHE",
+ "C'EST LE SQUELETTE TYPIQUE QU'ON TROUVE DANS LES GE\342LES DE TOUS LES JEUX",
+ "ON L'EMPLOIE NORMALEMENT POUR ENVOY\202 DU COURANT AUX APPAREILS QU'Y SONT RACCORD\220S",
+ "C'EST ARTISANAL. LES JAPONAIS LES FONT MAINTENANT DE POCHE",
// 130
- "J'AI SEULEMENT VU DANS MA VIE UNE CHOSE SI MOCHE",
- "LAISSE. JE NE LUI DIS RIEN POUR S'IL SE F\266CHE",
+ "J'AI SEULEMENT VU UNE FOIS DANS MA VIE QUELQUE CHOSE D'AUSSI MOCHE",
+ "LAISSE TOMB\202. JE NE LUI DIRAIS RIEN AU CAS O\353 IL SE F\266CHERAIT",
"IL SEMBLE ASSEZ RATIONNEL",
"C'EST UNE PHOTO DE PLATON EN TRAIN D'\220CRIRE SON DIALOGUE PERDU",
- "JE NE SUIS PAS DE CEUX QUI PARLENT AUX POSTERS",
+ "JE NE FAIS PAS PARTI DE CEUX QUI PARLENT AUX POSTERS",
// 135
"UN BUREAU ASSEZ MIGNON",
- "C'EST UN DIPL\342ME DE CHASSE-CHASSE-VAMPIRES HOMOLOGU\220 PAR L'UNIVERSIT\220 D'OXFORD",
- "C'EST UNE NUIT NOIRE AU PLEINE LUNE",
- "IL PARA\327T QUE CES VIS NE SONT PAS TR\324S ENFONC\220ES",
- "N'Y REGARDES PAS, MAIS JE CROIS QU'UNE CAM\220RA OCCULTE ME VISE",
+ "C'EST UN DIPL\342ME DE CHASSEUR DE VAMPIRES DE L'UNIVERSIT\220 D'OXFORD",
+ "C'EST UNE NUIT NOIRE AVEC UNE PLEINE LUNE",
+ "IL SEMBLE QUE CES VIS NE SONT PAS TR\324S BIEN ENFONC\220ES",
+ "NE REGARDES PAS, MAIS JE CROIS QU'UNE CAM\220RA CACH\202E ME VISE",
// 140
"UN D\220TECTEUR DE PIEUX ASSEZ MODERNE",
"NON, LE LABORATOIRE EST AU DEUXI\324ME \220TAGE",
"UNE JOLIE TABLE DE NUIT",
- "C'EST UN TAS D'ARGENT QUI NE PEUT PAS MANQUER DANS UNE AVENTURE DIGNE DE TEL NOM",
+ "C'EST UNE GROSSE SOMME D'ARGENT, INCONTOURNABLE DANS UNE AVENTURE DIGNE DE CE NOM",
"SI J'\220TAIS RICHE. DUBIDOUDUBIDOUDUBIDOUDUBIDOU",
// 145
- "CE SONT DES FEUILLES BIZARRES. ON A D\352 LES AMENER DE L'AM\220RIQUE DU SUD OU PAR L\267",
+ "CE SONT DES FEUILLES BIZARRES. ON A D\352 LES RAPPORTER DE L'AM\220RIQUE DU SUD OU DE CE COIN L\267",
"JE NE PENSE PAS QU'ILS VONT ME R\220PONDRE",
- "C'EST UN JOLI CRUCIFIX EN BOIS. L'IC\342NE N'ARRIVE PAS \267 SAISIR TOUT LA SPLENDEUR DE SA BEAUT\220",
+ "C'EST UN JOLI CRUCIFIX EN BOIS. L'IC\342NE NE MONTRE PAS VRAIMENT TOUTE SA SPLENDEUR",
"JE PRIE SEULEMENT AVANT DE ME COUCHER",
- "H\220!, IL PARA\327T QUE CETTE PIQUE S'EST UN PETIT PEU D\220CROCH\220E",
+ "H\220!, CETTE PIQUE SEMBLE MAL FIX\202!",
// 150
- "TU NE POURRAS TE PLAIGNER APR\324S DU PEU DE PISTES QUE JE TE DONNE",
+ "J'ESP\212RE QUE TU NE VAS PLUS TE PLEINDRE QUE JE NE TE DONNE PAS D'INDICES",
"C'EST UNE PIQUE ASSEZ CONVENTIONNELLE",
"ILS SONT MIGNONS, MAIS UN PEU SALES",
- "NON, NON, ILS NE ME \220COUTERAIENT PAS. HI, HI, HI -QUE C'EST BON!",
- "\"LA BELLE DORMANTE DU BOIS\" DE TCHA\330KOVSKI, OU TCHA\330FROSKI, OU N'IMPORTE COMMENT DIT-ON ",
+ "NON, ILS NE M'ENTENDRONS PAS. HA, HA, HA C'EST G\202NIALE!",
+ "\"LA BELLE AU BOIS DORMANTE\" DE TCHAIKOVSKI, OU TCHAIFROSKI, OU... OH ET PUIS ZUT",
// 155
"TR\324S APP\220TISSANT",
- "JE NE SUIS PAS DE CEUX QUI SUCCENT DES CHEWING-GUMS D\220J\267 M\266CH\220S",
- "UNE FAUCILLE TR\324S MIGNONE. jE ME DEMANDE O\353 SERA LE MARTEAU?",
+ "JE NE MET PAS DES CHEWING-GUMS D\220J\267 M\266CH\220S DANS MA BOUCHE",
+ "UNE FAUCILLE TR\324S MIGNONE. jE ME DEMANDE O\353 EST LE MARTEAU.",
"\"LES FABRICANTS DE TABAC AVERTISSENT QUE LES AUTORIT\220S SANITAIRES SONT S\220RIEUSEMENT NUISIBLES POUR LA SANT\220 \"",
- "UNE BOUGIE COURANTE ET NORMALE ET AVEC DE LA CIRE M\322ME",
+ "UNE BOUGIE COMPL\202TEMENT NORMALE, AVEC DE LA CIRE ET TOUT LE RESTE",
// 160
- "IL FAUT VOIR COMME ELLES LUISENT CES DEUX RUTILANTES MONNAIES!",
- "IL FAUT VOIR COMME ELLE LUIT CETTE RUTILANTE MONNAIE!",
+ "IL FAUT VOIR COMME ELLES BRILLENT CES DEUX RUTILANTES PI\212CES!",
+ "IL FAUT VOIR COMME ELLE BRILLE CETTE RUTILANTE PI\212CE!",
"AVEC \200A JE SERAI IMMUNIS\220 CONTRE LES MORSURES DES VAMPIRES",
- "NON, CE N'EST PAS ENCORE LE MOMENT",
- "IL Y A UN BILLET DE MILLE ET DEUX SOUS",
+ "NON, CE N'EST PAS ENCORE LE BON MOMENT",
+ "IL Y A UN BILLET DE MILLE ET DEUX PI\212CES",
// 165
- "ON DIT\"VOUS \322TES PRI\220S DE NE PAS DONNER \267 MANGER AU PIANISTE\"",
- "L'OMELETTE, 200. DES PETITS POISSONS FRITS, 150, DES POMMES A\330OLI, 225",
- "LES MEILLEURES HAMBURGERS DE CE C\342T\220 DU DANUBE, SEULEMENT 325",
- "C'EST UNE JOLIE T\322TE DE MORT AU REGARD TR\324S PER\200ANT -HI, HI, HI, QUE C'EST BON!",
+ "IL EST \202CRIT \"VOUS \322TES PRI\220S DE NE PAS DONNER \267 MANGER AU PIANISTE\"",
+ "L'OMELETTE, 1.00. POISSONS FRITS, 0.80, POMMES A\330OLI, 1.10",
+ "LES MEILLEURES HAMBURGERS DE CE C\342T\220 DU DANUBE, SEULEMENT 325!",
+ "C'EST UNE JOLIE T\322TE DE MORT AU REGARD TR\324S PER\207ANT HI, HI, HI, QU'ELLE EST BONNE!",
"BONJOUR T\322TE DE MORT, TU ME RAPPELLES L'ONCLE HAMLET",
// 170
- "J'AI LA HABITUDE DE NE PAS TOUCHER AUX CHOSES QUI ONT V\220CU AUTREFOIS",
+ "J'AI POUR HABITUDE DE NE PAS TOUCHER AUX CHOSES QUI ONT V\220CU AUTREFOIS",
"C'EST UNE POUBELLE",
- "C'EST UNE MASSUE POUR LE MATCH DE CE SOIR",
- "JE ME DEMANDE CE QU'IL AURA DERRI\324RE",
+ "C'EST UN PARI POUR LE MATCH DE CE SOIR",
+ "JE ME DEMANDE CE QU'IL Y A DERRI\324RE",
"H\220, CE RIDEAU NE BOUGE PAS!",
// 175
- "TIENS, QUEL CH\266TEAU SI SOMBRE, H\220? ",
+ "MEC, CE CH\266TEAU EST VRAIMENT SINISTRE",
"JE NE PEUX PAS, IL EST TROP LOIN POUR M'ENTENDRE",
- "C'EST UNE TYPIQUE FOR\322T TRANSYLVANE, AVEC DES ARBRES",
- "MAIS TU NE DIS QUE DES B\322TISES, C'EST UN LIEU OBSCUR!",
- "CONFISERIE GARCIA, G\266TEAUX ET CHEWING-GUMS.",
+ "C'EST UNE FOR\322T TYPIQUE TRANSYLVANIENNE, AVEC DES ARBRES",
+ "MEC, TU DIS VRAIMENT DES B\322TISES, CETTE PI\212CE EST TROP SOMBRE!",
+ "CONFISERIE GARCIA, BONBONS ET CHEWING-GUMS.",
// 180
"UNE PORTE TR\324S JOLIE",
"ELLE EST FERM\220E",
- "UN TONNEAU COMPL\324TEMENT FERM\220",
+ "UN TONNEAU COMPL\324TEMENT SCELL\220",
"",
- "QUELLES BESTIOLES SI MIGNONES!",
+ "QUELLES MIGNONES PETITES BESTIOLES!",
// 185
"BSSST, BSSST, PETIT CHAT...",
"IL NE R\220POND PAS",
"LA LUNE EST UN SATELLITE TOURNANT AUTOUR DE LA TERRE AVEC UNE P\220RIODE DE ROTATION DE 28 JOURS",
"SALUT, LUNE!, LUN\220E ET \220TOURDIE ",
- "ELLE EST COMPL\324TEMENT BOUCH\220E AVEC DES GROSSES PLANCHES",
+ "ELLE EST COMPL\324TEMENT OBTUR\202E AVEC DES GROSSES PLANCHES",
// 190
- "C'EST IMPOSSIBLE, CECI NE L'OUVRE PAS NI LE MAJORDOME DE LA T\220L\220",
- "H\220, IL PARA\327T QUE L'OMBRE DE CE CYPR\324S-LA EST ALLONG\220E",
+ "C'EST IMPOSSIBLE. M\322ME LE GARS COSTAUD DE LA T\220L\220 NE POURRAI PAS L'OUVRIR",
+ "H\220, L'OMBRE DE CE CYPR\324S ME SEMBLE ALLONG\220E!",
"OH\220! H\342TELIEEER!",
"JE VOUDRAIS UNE CHAMBRE",
- "SAVEZ-VOUS O\353 EST QUE JE PEUX TROUVER UN TEL COMTE DRASCULA?",
+ "SAVEZ-VOUS O\353 PUIS-JE TROUVER LE SIEUR DRASCULA?",
// 195
- "OUI, QU'EST-CE QU'IL Y A? ",
- "ET \200A?",
- "EN... EN V\220RIT\220?",
- "UNE BONNE QUESTION, JE VAIS VOUS RACONTER MON HISTOIRE, TIENS...",
- "JE N'AI QUE POUR CINQ MINUTES",
+ "OUI, ET ALORS? ",
+ "ET ALORS?",
+ "EST-CE... VRAIS?",
+ "BONNE QUESTION, TIENS JE VAIS VOUS RACONTER MON HISTOIRE...",
+ "JE N'EN AI QUE POUR CINQ MINUTES",
// 200
- "JE M'APPELLE JOHN HACKER ET SUIS LE REPR\220SENTANT D'UNE INMOBILI\324RE BRITANNIQUE",
- "IL PARA\327T QUE LE COMTE DRASCULA VEUT ACHETER DES TERRAINS \267 GIBRALTAR ET ON M'A ENVOY\220 POUR N\220GOCIER L'AFFAIRE",
- "MAIS JE PENSE QUE DEMAIN DE BONNE HEURE JE RETOURNE AVEC MA MAMAN",
+ "JE M'APPELLE JOHN HACKER ET SUIS LE REPR\220SENTANT D'UNE COMPANIE INMOBILI\324RE BRITANNIQUE",
+ "POUR AUTANT QUE JE S\266CHE LE COMTE DRASCULA VEUT ACHETER DES TERRAINS \267 GIBRALTAR ET ON M'A ENVOY\220 POUR N\220GOCIER LA VENTE",
+ "JE CROIS QUE DEMAIN DE BONNE HEURE JE RETOURNE CHEZ MA MAMAN",
"UNE BELLE NUIT, N'EST-CE PAS?",
"NON, RIEN",
// 205
"OH\220! PIANISTE!",
"UNE BELLE NUIT",
- "ET EN PLUS, IL NE FAIT PAS FROID",
- "EH BIEN, RIEN. CONTINUE \267 JOUER",
+ "ET IL NE FAIT M\322ME PAS FROID. EN FAIT, NE POURRIEZ-VOUS PAS CHANGER DE MORCEAU?",
+ "D'ACCORD. JE VOUS LAISSE JOUER",
"C'EST \200A",
// 210
- "BONJOUR CHEF, \200A VA?",
+ "BONJOUR CHEF, \207A VA?",
"ET LA FAMILLE?",
"IL Y A DE L'AMBIANCE ICI, H\220?",
- "TANT MIEUX SI JE NE DIS RIEN",
- "ON EST MIEUX CHEZ-SOI QU'AILLEURS... ON EST MIEUX DANS... H\220? MAIS VOUS N'\322TES PAS TANTE EMMA. MIEUX ENCORE. -SI JE N'AI PAS AUCUNE TANTE EMMA!",
+ "JE FERAIS MIEUX DE NE RIEN DIRE",
+ "ON EST MIEUX CHEZ-SOI QU'AILLEURS... ON EST MIEUX... H\220? MAIS VOUS N'\322TES PAS TANTE EMMA. D'AILLEURS JE N'AI PAS DE TANTE EMMA!",
// 215
- "OUI, LE MIEN AUSSI. VOUS POUVEZ M'APPELLER COMME VOUS VOULEZ, MAIS SI VOUS M'APPELLEZ JOHNY, JE VIENS COMME LES CHIENS",
- "OUI, QUELS COUPS QUE J'AI, N'EST-CE PAS? EN FAIT, O\353 SUIS-JE?",
+ "VOUS POUVEZ M'APPELLER COMME VOUS VOULEZ, MAIS SI VOUS M'APPELLEZ JOHNNY, J'ACCOURERAI COMME UN CHIEN",
+ "NE SUIS-JE PAS MARRANT, HEIN? EN FAIT, O\353 SUIS-JE?",
"OUI",
"A\330E!, A\330E!...",
- "OH, OUI! BIEN S\352R",
+ "EUH, OUI... BIEN S\352R",
// 220
- "EH BIEN! MERCI BEAUCOUP POUR TON AIDE. JE NE TE D\220RANGE PLUS SI TU ME DIS O\353 SE TROUVE LA PORTE, S'IL TE PLA\327T...",
- "CAR LE COUP A D\352 ME TOUCHER LA CERVELLE ET JE N'Y VOIS GOUTTE",
- "BAH!, \200A FAIT RIEN. J'AI TOUJOURS UNE DE R\220CHANGE",
- "OUAH, QUELLE BELLE FEMME! -JE NE M'AVAIS PAS RENDU COMPTE! BIEN S\352R, SANS LES LUNETTES...",
+ "EH BIEN! MERCI BEAUCOUP POUR TON AIDE. JE NE TE D\220RANGERAI PAS PLUS SI TU ME DIS O\353 SE TROUVE LA PORTE...",
+ "LE COUP A D\352 AFFECTER MON CERVAUX ET JE NE VOIS RIEN...",
+ "BAH!, \207A FAIT RIEN. J'EN AI TOUJOURS UNE DE R\220CHANGE",
+ "OUAH, ELLE EST CANON! JE NE M'\202TAIS PAS RENDU COMPTE! MAIS BIEN S\352R, SANS MES LUNETTES...",
"\220COUTE...",
// 225
- "ET \200AAAAAA?!",
- "NE T'EN FAIS PAS B. J., MON AMOUR! JE VAIS TE SAUVER DES GRIFFES DE CELUI-L\267 ",
- "IL ME CASSE LE NEZ, TIENS!",
- "AHHH, UN HOMME-LOUP! MEURS MAUDIT!",
+ "ET MAINTENANT \207AAAAAA?!",
+ "NE T'EN FAIS PAS B. J., CH\202RI! JE VAIS TE SAUVER DE SES GRIFFES...",
+ "TU M'AS VRAIMENT MIS EN COL\212RE MEC!",
+ "AHHH, UN LOUP-GAROU! MEURS MAUDIT!",
"OUI, C'EST CELA...",
// 230
- "OUI, C'EST CELA... JE CROIS QUE JE VAIS SUIVRE MON CHEMIN. PARDON.. ",
+ "OUI, C'EST CELA... JE CROIS QUE JE VAIS SUIVRE MON CHEMIN. EXCUSEZ-MOI.. ",
"QUOI?",
- "EH BIEN, EN V\220RIT\220, BIEN PENS\220... JE NE CROIS PAS",
- "DIS -MOI, OH! \220RUDITE PHILOSOPHE! Y A-T-IL UNE R\220LATION CAUSE-EFFET ENTRE LA VITESSE ET LE LARD?",
- "\200A VA, \200A VA, ABANDONNE. EN TOUT CAS, JE NE SAIS PAS POURQUOI JE L'AI DIT.",
+ "EH BIEN, POUR TE DIRE LA V\220RIT\220...APR\212S R\202FLECTION... JE NE CROIS PAS",
+ "DIS-MOI, OH \220RUDIT PHILOSOPHE! Y A-T-IL UNE RELATION DE CAUSE \205 EFFET ENTRE BEN\322T ET BENOIT?",
+ "OK, OK, OUBLIE. JE NE SAIS M\322ME PAS POURQUOI J'AI DEMANDAIS \207A.",
// 235
- "QU'EST-CE QUE TU FAIS ICI EN TRAIN DE PHILOSOPHER AU LIEU DE MANGER DU MONDE?",
- "QU'EST-CE QUE C'EST QUE \200A?",
- "\220COUTE, PEX-TU R\220P\220TER CETTE PHRASE DES \"INCLINATIONS PR\220-\220VOLUTIVES\"?",
- "BIEN S\352R, MON VIEUX. CETTE HISTOITE QUE TU M'AS LACH\220E AVANT. CE QUE JE N'AI PAS BIEN COMPRIS...",
- "NON, LE MIEUX SERA DE NE RIEN DIRE. CAR SI JE LE TOUCHE LA VEINE.....",
+ "QUE FAIS-TU ICI \205 PHILOSOPHER AU LIEU DE MANGER DU MONDE?",
+ "COMMENT \207A?",
+ "H\220, PEUX-TU R\220P\220TER CETTE PHRASE SUR LES \"INCLINATIONS PR\220-\220VOLUTIVES\"?",
+ "BIEN S\352R, MEC. TOUS CES TRUCS DONT TU M'AS PARL\220 AVANT. JE N'AIS RIEN COMPRIS TU SAIS",
+ "NON, JE FERAIS MIEUX DE ME TAIRE. AU CAS O\227 IL SE F\266CHERAI...",
// 240
- "OUI, QUE SE PASSE-T-IL?",
- "OUI, QU'EST-CE QU'IL Y A? ",
- "EH BIEN, MAINTENANT QU'IL ABORDE LE SUJET JE LUI DIRAI QUE...",
+ "ALLO?",
+ "OUI, QUOI DE NEUF? ",
+ "EH BIEN, MAINTENANT QUE TU LE MENTIONE, JE VAIS TE DIRE QUE...",
"",
- "EN FAIT, QU'ARRIVERAIT-IL SI UN VAMPIRE SE POURVOYAIT DE LA FORMULE PAR HASARD... ",
+ "EN FAIT, C'EST JUSTE UNE HYPOTH\212SE BIEN S\352R, MAIS QU'ARRIVERAIT-IL SI UN VAMPIRE TROUVAI LA FORMULE PAR HASARD?",
// 245
- "EH BIEN, RIEN. \220COUTE, CECI NE TE SEMBLE PAS UN RAVAUDAGE QU'ON A MIS EN SC\324NE POUR EN FINIR T\342T AVEC LE JEU? BON, PEUT-\322TRE PAS",
+ "EH BIEN, PASSONS. \220COUTE, CEL\267 NE TE SEMBLE T-IL PAS \210TRE UN PAQUET DE B\202TISES POUR FINIR LE JEU? BON, PEUT-\322TRE PAS",
"C'EST VIDE!",
- "POURQUOI TU M'AS VOL\220 MON AMOUR. B.J. SI ELLE N'EST PAS L\267, LA VIE N'AS PAS DE SENS POUR MOI",
+ "POURQUOI TU M'AS VOL\220 MON AMOUR. B.J. LOIN DE MOI? SANS ELLE LA VIE N'AS PAS DE SENS POUR MOI",
"SON CERVEAU?!",
- "CE N'EST POUR RIEN, MAIS JE CROIS QUE TON PETIT MONSTRE M'A F\266CH\220",
+ "POUR TE DIRE LA V\202RIT\202, JE CROIS QUE JE ME SUIS D\202J\205 ASSEZ AMUS\202 AVEC TON PETIT MONSTRE",
// 250
- "MA VIERGE, QUE JE RESTE TEL QUE JE SUIS!",
- "TU N'AURAS PAS LE DERNIER MOT. C'EST S\352R QUE MAINTENANT APPARA\327T SUPER-LOPEZ ET ME LIB\324RE!",
- "QUELLE MERDE DE JEU DONT MEURT LE PROTAGONISTE!",
- "UN INSTANT, QU'Y A-T-IL DE MON DERNIER D\220SIR?",
- "HA! HA! MAINTENANT JE SUIS IMMUNIS\220 CONTRE TOI, D\220MON MAUDIT. CETTE CIGARETTE EST UNE POTION ANTI-VAMPIRES QUI M'A DONN\220 VON BRAUN ",
+ "PITI\202 SAINTE VIERGE, FAITES QUE RIEN DE PIRE NE M'ARRIVE!!",
+ "TU N'AURAS PAS LE DERNIER MOT. JE SUIS S\352R QUE SUPERMAN VAS VENIR ME LIB\324RER!",
+ "QUELLE MERDE CE JEU DANS LEQUEL LE PERSONNAGE PRINCIPAL MEURT!",
+ "H\202 UN INSTANT! ET MON DERNIER SOUHAIT?",
+ "HA! HA! MAINTENANT JE SUIS IMMUNIS\220 CONTRE TOI, D\220MON. CETTE CIGARETTE EST UNE POTION ANTI-VAMPIRES QUE M'A DONN\220 VON BRAUN ",
// 255
- "OUI, C'EST S\352R, MAIS TU N'OBTIENDRAS JAMAIS DE MOI LA FORMULE",
- "JE PEUX SUPPORTER LA TORTURE, ET ENCORE LA CR\220ER ",
- "NON, S'IL VOUS PLA\327T, JE PARLERAI, MAIS NE ME FAITES PAS \200A!",
- "EH BIEN. JE T'AI D\220J\267 DIT CE QUE TU VOULAIS SAVOIR. MAINTENANT D\220LIVRE-NOUS, B.J. ET MOI, ET FICHEZ-NOUS LA PAIX",
- "B.J.! QU'EST-CE QUE TU FAIS L\267? DRASCULA, O\353 EST-IL?",
+ "OUI, C'EST S\352R, MAIS JE NE TE DONNERAIS JAMAIS LA FORMULE",
+ "A PART CR\202ER LA TORTURE JE PEUX AUSSI LA SUPPORTER",
+ "NON, PAR PITI\202, JE PARLERAI, MAIS NE ME FAITES PAS \200A!",
+ "D'ACCORD. JE T'AI DIT CE QUE TU VOULAIS SAVOIR. MAINTENANT LIB\212RE-NOUS, B.J. ET MOI, ET FICHE-NOUS LA PAIX",
+ "B.J.! QUE FAIS-TU ICI? O\353 EST DRASCULA?",
// 260
- "QU'IL EST M\220CHANT! C'EST SEULEMENT PAR-CE QU'IL APPARTIENT \267 LA NOBLESSE QU'IL CROIT POUVOIR EXERCER LE DROIT DE GAMBADE AVEC N'IMPORTE QUI",
- "\267 BAS L'ARISTOCRATIE ARBITRAIRE!",
+ "QU'IL EST M\220CHANT! PAR-CE QU'IL APPARTIENT \267 LA NOBLESSE IL CROIT POUVOIR COUCHER AVEC QUI IL VEUT",
+ "\267 BAS LE DESPOTISME ARISTOCRATIQUE!",
"DEBOUT LES PAUVRES DU MOOONDE....!",
- "ET D'APR\324S CE QUE JE VOIS ON T'A ENCHA\327N\220 AVEC CADENAS ET TOUT",
- "BON, \200A VA. N'AURAS-TU PAS UNE \220PINGLE?",
+ "ET D'APR\324S CE QUE JE VOIS IL T'A ENCHA\327N\220 AVEC CADENAS ET TOUT, H\202?",
+ "BON, OK. TU N'AURAIS PAS UNE \220PINGLE?",
// 265
- "BON, BON, NE T'EN FAIS PAS COMME \200A. JE PENSERAI \267 QUELQUE CHOSE.",
- "H\220! TAVERNIER!",
- "COMMENT VA LE MATCH?",
- "QUI EST-CE?",
+ "BON, BON, OK, RESTE CALME. JE VAIS TROUVER QUELQUE CHOSE.",
+ "H\220! TAVERNIER!!",
+ "O\227 EN EST LE MATCH?",
+ "QUI?",
"NE VOIS-TU PAS QUE DRASCULA EST ICI?",
// 270
- "ALORS, ON VA FINIR AVEC LUI, NON?",
- "SERS -MOI UN COUP...",
- "RIEN. J'AI OUBLI\220 CE QUE J'ALLAIS TE DIRE",
- "OU\207BIEN\207TU\207ME\207SERS\207UN\207COUP\207OU\207JE\207ME\207METS\207\267\207JOUER\207DU\207PIANO",
- "COMBIEN IL RESTE POUR QUE LE MATCH FINISSE?",
+ "ALORS, FINISSONS EN AVEC LUI, NON?",
+ "SERS-MOI UN COUP...",
+ "RIEN. J'AI OUBLI\220 CE QUE J'ALLAIS DIRE",
+ "OU BIEN TU ME SERS UN COUP OU BIEN JE JOUE DU PIANO JUSQU'\267 LA FIN DU MATCH",
+ "COMBIEN DE TEMP IL RESTE JUSQU'\267 LA FIN DU MATCH?",
// 275
- "BON SOIR",
- "COMME VAS-TU, IGOR? BOSSU? -HI! HI! HI! QUE C'EST BON! ",
- "QU'EST-CE QU'ON SUPPOSE QUE TU FAIS?",
- "EH BIEN, NON!",
- "ALORS, METS-TOI DES LUNETTES",
+ "BONSOIR",
+ "COMME \200A VAS, IGOR? BOSSU? HI! HI! HI! C'\202TAIT DR\342LE, NON?",
+ "QU'EST-CE QUE TU ES SUPPOS\202 FAIRE?",
+ "EUH, NON! JE NE COMPREND RIEN AUX TAXES",
+ "ALORS, METS DES LUNETTES",
// 280
- "QU'EST QUE C'EST QU'UNE ORGIE SURNATURELLE?",
- "\200A VA, \200A VA, ARR\322TE-TOI. JE ME FAIS D\220J\267 UNE ID\220E",
- "NE POURRAIS-TU PAS ME DIRE O\353 SE TROUVE DRASCULA? ",
- "ALLONS, S'IL TE PLA\327T",
+ "C'EST QUOI UNE ORGIE SURNATURELLE?",
+ "OK, OK ARR\210TE. JE CROIS QUE J'AI COMPRIS",
+ "POURRAIS-TU ME DIRE O\353 SE TROUVE DRASCULA? ",
+ "ALLEZ, S'IL TE PLA\327T",
"POURQUOI PAS?",
// 285
- "AH! MAIS IL DORME PENDANT LA NUIT?",
- "EH BIEN! QUE LA RENTE SE DONNE BIEN",
- "CE QUE JE DOIS LUI PARLER",
+ "OH...IL DORT PENDANT LA NUIT?",
+ "EH BIEN! QUE LA RENTE SE PORTE BIEN",
+ "C'EST QUE JE DOIS LUI PARLER...",
"OH\220! SQUELEEETTE! ",
- "SAPRISTI! -UN SQUELETTE QUI PARLE!",
+ "SAPRISTI! UN SQUELETTE QUI PARLE!",
// 290
- "RACONTE-MOI, COMMENT EST-TU VENU JUSQU'ICI?",
- "ET POUR QUELLE RAISON VOUDRAIT DRASCULA CR\220ER UN MONSTRE? ",
+ "COMMENT EST-TU ARRIV\202 JUSQU'ICI?",
+ "ET POURQUOI DRASCULA VOUDRAIT-IL CR\220ER UN MONSTRE?",
"COMMENT T'APPELLES-TU, AMI SQUELETTE?",
- "\220COUTE, VEUX-TU QUE JE T'APPORTE QUELQUE CHOSE \267 MANGER?",
- "TU DOIS AVOIR L'ESTOMAC VIDE. -HI! HI! HI!",
+ "H\202, TU N'AS PAS FAIM?",
+ "TU DOIS AVOIR L'ESTOMAC VIDE... HI! HI! HI!",
// 295
- "VRAIMENT JE N'AI PAS ENVIE DE PARLER MAINTENANT",
- "MON DIEU! (SIFFLEMENT) J'ESP\324RE QUE...(SIFFLEMENT) ET QUE...(SIFFLEMENT) DEUX FOIS!",
- "J'AI L'AIM\220E VRAIMENT. \200A VA, JE SUIS D'ACCORD, IL N'\220TAIT PAS UN G\220NIE, MAIS PERSONNE EST PARFAIT, N'EST-CE PAS? ",
- "DE PLUS, ELLE AVAIT UNE FIGURE \220POUSTOUFLANTE ",
- "JE NE SERAI PLUS LE M\322ME. JE VAIS M'ENFERMER DANS UN MONAST\324RE POUR VOIR FUIR MA VIE LENTEMENT",
+ "JE N'AI PAS ENVIE DE PARLER MAINTENANT",
+ "J'ESP\324RE QUE QUELQU'UN VA T'ENC...(SIFFLEMENT) TOI ET TON P...(SIFFLEMENT) DE FILS DE (SIFFLEMENT)!",
+ "JE L'AIM\220E VRAIMENT. C'EST VRAIS QUE CE N'\220TAIT PAS UN G\220NIE, MAIS PERSONNE N'EST PARFAIT, N'EST-CE PAS? ",
+ "DE PLUS, ELLE AVAIT DES FORMES \220POUSTOUFLANTES",
+ "JE NE SERAI PLUS JAMAIS LE M\322ME. JE VAIS M'ENFERMER DANS UN MONAST\324RE POUR VOIR MA VIE FUIR LENTEMENT",
// 300
- "RIEN NE POURRA M'EN SORTIR D\220J\267 DE CETTE MIS\324RE PARCE QUE...",
+ "RIEN NE POURRA ME FAIRE SORTIR DE CETTE MIS\324RE PARCE QUE...",
"DE QUI? DE QUI?",
"JE VEUX \322TRE UN PIRATE",
"JE VEUX \322TRE PROGRAMMEUR",
"RACONTEZ-MOI QUELQUE CHOSE SUR PELAYO",
// 305
- "JE CONTINUERAI \267 JOUER ET J'OUBLIERAI QUE VOUS AI VU ",
- "QUI AURA PENS\220 \267 CETTE B\322TISE?",
- "C'EST UN SAC COMME CELUI DE MA GRANDE-M\324RE",
- "MAIS QUE JE SUIS BEAU!",
+ "JE VAIS JUSTE CONTINUER \267 JOUER ET OUBLIER QUE JE T'AI VU",
+ "QUI A EU CETTE ID\202E STUPIDE?",
+ "\200A RESSEMBLE AU SAC DE MA GRANDE-M\324RE",
+ "WHAOU! NE SUIS-JE PAS MAGNIFIQUE!",
"PLUS JE ME REGARDE PLUS JE ME PLAIS",
// 310
"ET APR\324S COMMENT JE ME FERME?",
- "IL FAUDRA QUE M'OUVRE D'ABORD, NON?",
+ "IL FAUDRA QUE JE M'OUVRE D'ABORD, NON?",
"JE SUIS BIEN O\353 JE SUIS",
"JE M'AI D\220J\267",
"SALUT, MOI!",
// 315
- "JE VAIS ME LES METTRE \267 TEMPS",
+ "JE VAIS LES METTRE LE TEMPS VENU",
"JE NE VOIS RIEN DE SP\220CIAL",
- "C'EST BIEN O\353 IL EST",
+ "IL EST BIEN L\205 O\353 IL EST",
"ET POURQUOI FAIRE?",
"JE NE PEUX PAS",
// 320
"SALUT, TOI!",
"C'EST LE PANTH\220ON DE L'ONCLE D\220SIR\220",
"OH\220! ONCLE D\220SIR\220\220\220\220!",
- "NON, JE NE VEUX PAS ME COUPER ENCORE UNE FOIS",
- "HEM! HEM!...!",
+ "NON, JE NE VEUX PAS ME COUPER ENCORE",
+ "HEM! EXCUS...!",
// 325
"YAMM, HEMMM, JH!",
"OUI, COF,COF!",
- "TIENS, IL Y A ICI UN CHEWING-GUM COLL\220",
- "C'EST LE MOVILANI, LE CADEAU QUI M'ONT DONN\220 POUR NO\323L",
+ "TIENS, IL Y A UN CHEWING-GUM COLL\220 ICI",
+ "C'EST LE PORTABLE QUE J'AI EU POUR NOEL",
"QUE C'EST HAUT!",
// 330
- "SORS DANS LE BALCON JULIETTE!",
- "TU EST LA LUMI\324RE QUI \220CLAIRE MON CHEMIN!",
+ "SORS SUR LE BALCON MA JULIETTE!",
+ "TU ES LA LUMI\324RE QUI \220CLAIRE MON CHEMIN!",
"H\220, PORTE! QU'EST-CE QU'IL Y A?",
"OH\220! MACHINE \267 TABAC DE TRANSYLVANIIIE",
- "C'EST UNE MACHINE \267 D\220BIT DE TABAC",
+ "C'EST UN DISTRIBUTEUR DE PAQUET DE CIGARETTES",
// 335
- "J'AI UNE AUTRE MONNAIE L\267 -DEDANS",
- "NON, J'AI D\220CID\220 ABANDONNER LE TABAC ET L'ALCOOL",
- "D\324S MAINTENANT JE VAIS ME CONSACRER SEULEMENT AUX FEMMES",
- "C'EST UN VOL! RIEN EST SORTI!",
- "ENFIN! ",
+ "J'AI UNE AUTRE PI\212CE L\267 -DEDANS",
+ "NON, J'AI D\220CID\220 D'ARR\210TER LE TABAC ET L'ALCOOL",
+ "\205 PARTIR DE MAINTENANT JE VAIS ME CONSACRER SEULEMENT AUX FEMMES",
+ "C'EST DU VOL! RIEN EST SORTI!",
+ "ENFIN!",
// 340
- "C'EST \200A, UN BAHUT",
- "SALUT, BAHUT! TU T'APPELLES COMME MON COUSIN, QUI S'APPELLE RAUL",
+ "C'EST JUSTE UN COFRE",
+ "SALUT, COFFRE! TU T'APPELLES COMME MON COUSIN GEOFFREY...",
"J'AI TROUV\220 LE SAC DE B.J..",
- "MON DIEU! JE N'Y ME VOIS PAS -SUIS UN VAMPIRE!",
+ "MON DIEU! JE N'AI PAS DE REFLET. JE SUIS UN VAMPIRE!",
"...AH, NON! CE N'EST QU'UN DESSIN!",
// 345
- "PETIT MIROIR: \"C'EST QUI LE PLUS BEAU DU ROYAUME?\"",
- "IL NE VEUT PAS M'OUVRIR",
- "TR\324S BIEN. J'AI MIS LES TAMPONS",
- "C'EST UN DIPL\342ME DE CHASSE-VAMPIRES HOMOLOGU\220 PAR L'UNIVERSIT\220 DE CAMBRIDGE",
- "NON, IL ME FAUT ENCORE D'INGR\220DIENTS, PAS LA PEINE DE LUI REVEILLER",
+ "PETIT MIROIR, DIS MOI QUI EST LE PLUS BEAU DU ROYAUME?",
+ "IL NE VEUT PAS S'OUVRIR",
+ "TR\324S BIEN. J'AI MIS LES BOULES QUI\202S",
+ "C'EST UN DIPL\342ME DE CHASSEUR DE VAMPIRES DE L'UNIVERSIT\220 DE CAMBRIDGE",
+ "NON, IL ME MANQUE ENCORE DES INGR\220DIENTS, PAS LA PEINE DE LE REVEILLER",
// 350
"C'EST QUE JE SUIS FAUCH\220",
"C'EST UNE LAMPE BRITANNIQUE",
- "TAVERNIER! -AIDEZ-MOI!",
+ "TAVERNIER! AIDEZ-MOI!",
"UN VAMPIRE EST APPARU ET IL A ENLEV\220 MA FIANC\220E",
"MAIS, N'ALLEZ VOUS PAS M'AIDER?!",
// 355
"MORTE? QUE VOULEZ-VOUS DIRE?",
"HEM!",
- "UN VAMPIRE A S\220QUESTR\220E LA FILLE DE LA 506!",
+ "UN VAMPIRE A KIDNAP\202 LA FILLE DE LA 501!",
"IL FAUT QUE TU M'AIDES!",
- "TU NE SAIS PAS JOUER AUCUNE PI\324CE DES INHUMAINS?",
+ "TU NE POURRAIS PAS JOUER UNE CHANSON DE BLUR?",
// 360
- "COMMENT TU TE SUPPORTES TOUT LE JOUR EN JOUANT LA M\322ME CHOSE?",
- "ET ALORS, POURQUOI TU M'\220COUTES?",
- "PR\322TE-MOI LES TAMPONS",
- "ALLONS! JE VAIS TE LES REDONNER TOUT DE SUITE",
- "ALLOOONSSS...",
+ "COMMENT PEUX-TU RESTER L\205 TOUTE LA JOURN\202E \205 JOUER LE M\322ME MORCEAU?",
+ "ET ALORS, COMMENT TU PEUX M'ENTENDRE?",
+ "PR\322TE-MOI LES BOULES QUI\202S",
+ "ALLEZ! JE VAIS TE LES RENDRE TOUT DE SUITE",
+ "ALLEEEZZZ...",
// 365
- "AU REVOIR. JE DOIS TUER UN VAMPIRE",
+ "BON, AU REVOIR. JE DOIS TUER UN VAMPIRE",
"",
- "EN QUOI TU PARLES! EN TRANSYLVAN?",
- "C'EST QUI L'ONCLE D\220SIR\220?",
- "MAIS QU'EST-CE QU'IL Y A AVEC CE DRASCULA-L\267?",
+ "EN QUELLE LANGUE TU PARLES! TRANSYLVANIEN?",
+ "DE QUOI TU PARLES. C'EST QUI L'ONCLE D\220SIR\220?",
+ "MAIS, C'EST QUOI LE PROBL\212ME AVEC DRASCULA?",
// 370
- "QUI EST-CE CE VON BRAUN-L\267?",
+ "QUI EST CE VON BRAUN?",
"ET POURQUOI IL NE LE FAIT PAS?",
- "ET O\353 PEUX-JE TROUVER VON BRAUN?",
- "EH BIEN, MERCI ET AU REVOIR. QUE TU LA DORMES BIEN",
+ "ET O\353 PUIS-JE TROUVER VON BRAUN?",
+ "EH BIEN, MERCI ET AU REVOIR. CUVES BIEN",
"IL VAUDRA MIEUX SONNER D'ABORD",
// 375
- "LE PROFESSEUR VON BRAUN, C'EST VOUS?",
- "ET NE POUVEZ-VOUS PAS M'INDIQUER O\353 JE PEUX...?",
+ "\210TES-VOUS LE PROFESSEUR VON BRAUN?",
+ "ET POUVEZ-VOUS M'INDIQUER O\353 JE PEUX...?",
"JE NE CROIS PAS QU'IL SOIT LE NAIN GANYM\324DE",
"PROFESSEUR!",
- "AIDEZ-MOI! -LA VIE DE MA BIEN AIM\220E DEPENDE DE VOUS!",
+ "AIDEZ-MOI! LA VIE DE MA BIEN AIM\220E DEPEND DE VOUS!",
// 380
"\200A VA, JE N'AI PAS BESOIN DE VOTRE AIDE",
"D'ACCORD. JE M'EN VAIS",
- "N'AIES PAS PEUR. NOUS ALLONS VAINCRE DRASCULA ENSEMBLE",
+ "N'AYEZ PAS PEUR. NOUS ALLONS VAINCRE DRASCULA ENSEMBLE",
"ALORS, POURQUOI NE M'AIDEZ VOUS PAS?",
"JE LES AI",
// 385
"OUI, JE LES AI!",
"D'ACCORD",
"...ER ...OUI",
- "JE VIENS POUR RENTRER DANS CETTE CABINE",
- "SUIS PR\322T \267 CONFRONTER VOTRE \220PREUVE",
+ "JE VIENS POUR RETOURNER DANS VOTRE CABINE DE TORTURE",
+ "JE SUIS PR\322T \267 AFFRONTER VOTRE \220PREUVE",
// 390
- "\200A VA, VIEUX RIDICULE. JE SUIS VENU CHERCHER MON ARGENT",
- "NON, RIEN. JE M'EN ALLAIS D\220J\267",
- "PARDONNE-MOI",
- "CE LIVRE T'INT\220RESSE? AVEZ-VOUS DES PARTITIONS DE TCHA\330KOVSKY?",
- "COMMENT PEUX-JE TUER UN VAMPIRE?",
+ "\200A VA, VIEUX SCHNOC. JE SUIS VENU CHERCHER MON ARGENT",
+ "NON, RIEN. J'ALLAIS JUSTEMENT PARTIR",
+ "EXCUSES-MOI",
+ "CE LIVRE T'INT\220RESSE? IL CONTIENT DES PARTITIONS DE TCHAIKOVSKY?",
+ "COMMENT PUIS-JE TUER UN VAMPIRE?",
// 395
- "ON NE T'A JAMAIS DIT QUE C'EST MAUVAIS DORMIR DANS UNE MAUVAISE POSTURE?",
- "EH BIEN, C'EST \200A QUE MA M\324RE ME DIT TOUJOURS",
- "POURQUOI DRASCULA N'A PU JAMAIS TE TUER?",
- "ET QU'EST-CE QUE S'EST PASS\220?",
- "C'EST SUPER! -AVEZ-VOUS UNE POTION D'IMMUNIT...!",
+ "ON NE T'A JAMAIS DIT QUE C'EST MAUVAIS DE DORMIR DANS UNE MAUVAISE POSITION?",
+ "C'EST CE QUE MA M\324RE ME DIT TOUJOURS",
+ "POURQUOI DRASCULA N'A PAS PU TE TUER?",
+ "ET QUE S'EST-IL PASS\220?",
+ "C'EST SUPER! VOUS AVEZ UNE POTION D'IMMUNIT...!",
// 400
- "ALORS?",
+ "ET ALORS?",
"TR\324S BIEN",
"POUVEZ-VOUS ME R\220P\220TER CE DONT J'AI BESOIN POUR CETTE POTION?",
- "EH BIEN! JE PARS RAPIDE LE CHERCHER",
- "\220COUTEZ, QU'EST-CE QUE C'EST PASSE\220 AVEC LE PIANISTE?",
+ "EH BIEN! JE M'EN VAIS DE CE PAS EN CHERCHER",
+ "H\220, QUE S'EST-IL PASSE\220 AVEC LE PIANISTE?",
// 405
- "J'AI\207D\220J\267\207TOUS\207LES\207INGR\220DIENTS\207DE\207CETTE\207POTION",
- "UNE QUESTION: QU'EST.CE QUE C'EST CELA D' ALUCSARD ETEREUM?",
- "PARLEZ, PARLEZ... ",
- "ET C'EST O\353 CETTE GROTTE?",
- "QU'EST-CE QU'IL Y A? N'AVIEZ VOUS PAS UN TRIBUNAL?",
+ "J'AI TOUS LES INGREDIENTS POUR LA POTION",
+ "JUSTE UNE QUESTION: C'EST QUOI CET ALUCSARD ETEREUM?",
+ "OUI, OUI?... ",
+ "ET O\353 ELLE EST CETTE GROTTE?",
+ "QUE S'EST-IL PASS\202? N'AVIEZ VOUS PAS UN TRIBUNAL?",
// 410
- "...MAIS ...ET SI JE TROUVE ENCORE DES VAMPIRES?",
- "C'EST UN VAMPIRE QUI M'EMP\322CHE L'ENTR\220E",
- "IL RESSEMBLE \267 YODA, MAIS C'EST UN PEU PLUS GRAND",
- "H\220, YODA! SI TU ME LAISSES PASSER JE TE DONNE UNE MONNAIE",
+ "...MAIS ...ET SI JE RENCONTRE D'AUTRES VAMPIRES?",
+ "C'EST UN VAMPIRE QUI M'EMP\322CHE DE PASSER",
+ "IL RESSEMBLE \267 YODA, MAIS EN UN PEU PLUS GRAND",
+ "H\220, YODA! SI TU ME LAISSES PASSER JE TE DONNE UNE PI\212CE",
"BON, \200A VA. ON NE PEUT RIEN TE DIRE",
// 415
"H\220, VAMPIRE! BELLE NUIT, N'EST-CE PAS?",
"ON T'A D\220J\267 DIT QUE TU RESSEMBLES \267 YODA?",
"ES-TU UN VAMPIRE OU UNE PEINTURE \267 L'HUILE?",
- "IL VAUX MIEUX NE RIEN TE DIRE, POUR SI TU TE F\266CHES",
+ "JE FERAIS MIEUX DE ME TAIRE, TU ES SI SUSCEPTIBLE",
"C'EST FERM\220E \267 CL\220",
// 420
- "LA PIE POURRAIT M'ARRACHER UN OEIL SI J'EN ESSAIE!",
- "C'EST FERM\220E! -MON DIEU, QUELLE PEUR!",
- "LES GONDS SONT OXYD\220S",
- "L\267-DEDANS IL Y A SEULEMENT UN POT AVEC DE LA FARINE",
- "CECI A ENLEV\220 L'OXYDE",
+ "LA PIE POURRAIT M'ARRACHER UN OEIL SI J'ESSAIE!",
+ "MON DIEU. C'EST V\202ROUILL\202... C'EST \202FRAYANT HEIN?",
+ "LES GONDS SONT ROUILL\202S",
+ "IL Y A SEULEMENT UN POT DE FARINE L\267-DEDANS",
+ "\200A A ENLEV\220 LA ROUILLE",
// 425
- "J'AI TROUV\220 UN PIEU DE PIN",
- "JE PRENDRAI CELUI-CI QUI EST PLUS GROS",
- "BON, JE CROIS QUE JE PEUX ME D\220BARRASSER MAINTENANT DE CE STUPIDE D\220GUISSEMENT",
- "LE PASSAGE AUX DONJONS EST FERM\220 \267 CAUSE DES TRAVAUX. VOUS \322TES PRI\220S D'UTILISER L'ENTR\220E PRINCIPALE. EXCUSEZ LES ENNUIES",
- "...IL EST P\266LE. AVEC DE GROSSES DENTS. IL A UN TOUPET ET UTILISE UNE CAPE... -C'EST S\352REMENT DRASCULA!",
+ "J'AI TROUV\220 UN PIEU EN PIN",
+ "JE VAIS PRENDRE LE PLUS GROS L\267",
+ "BON, JE CROIS QUE JE PEUX ME D\220BARRASSER DE CE STUPIDE D\220GUISEMENT MAINTENANT",
+ "LE PASSAGE VERS LES DONJONS EST FERM\220 POUR CAUSE DE TRAVAUX. VOUS \322TES PRI\220S D'UTILISER L'ENTR\220E PRINCIPALE. D\202SOL\202 POUR LE D\202SAGR\202MENT",
+ "...IL EST P\266LE. AVEC DE GROSSES DENTS. IL A UN TOUPET ET PORTE UNE CAPE...C'EST S\352REMENT DRASCULA!",
// 430
- "C'EST B.J.! B.J. TU EST BIEN?",
- "OUI, JE SAIS QU'ELLE EST B\322TE, MAIS JE SUIS SEUL",
- "N'AURAS-TU PAS UNE CL\220 PAR L\267, N'EST-CE PAS?",
- "N'AURAS-TU PAS UN ROSSIGNOL, PAR HASARD?",
+ "C'EST B.J.! B.J. TU VAS BIEN?",
+ "OUI, JE SAIS ELLE EST B\322TE, MAIS JE ME SENS TELLEMENT SEUL",
+ "TU N'AURAIS PAS UNE CL\220 PAR L\267?",
+ "TU N'AURAIS PAS UN OUTIL DE CROCHETAGE, PAR HASARD?",
"DONNE-MOI UNE \220PINGLE. JE VAIS FAIRE COMME MCGYVER",
// 435
"NE BOUGES PAS, JE REVIENS TOUT DE SUITE",
- "ZUT! -S'EST CASS\220E!",
- "OL\220\220\220! ET EN PLUS JE ME SUIS RAS\220, COLL\324GUE!",
+ "ZUT! C'EST CASS\220E!",
+ "OL\220\220\220! JE ME SUIS M\210ME RAS\220, MEC!",
"OUI, MON AMOUR?",
- "IL N'ARRIVE PAS",
+ "IL N'EST PAS ENCORE ARRIV\202",
// 440
"LE PIANISTE N'EST PAS L\267",
- "UN COKTAIL TRANSYLVAN",
- "JE N'AI PAS UNE CHAMBRE",
- "SELON PARA\327T, IL EST REST\220 COINC\220 DANS LA BAIGNOIRE ET D\220CIDA ALORS D'OUVRIR UN BAR ",
- "IL EST SO\352L COME UNE CUVE DE CUBA",
+ "UN COKTAIL TRANSYLVANIEN",
+ "JE N'AI PAS ENCORE DE CHAMBRE",
+ "ON DIRAIT QU'IL EST REST\220 COINC\220 DANS UNE BAIGNOIRE ET A ALORS D\220CID\220 D'OUVRIR UN BAR",
+ "IL EST SO\352L COME UN MARIN",
// 445
- "CE CHEVEU... LE CAS CE QU'IL ME RAPPELLE QUELQU'UN",
+ "CE CHEVEU... \200A ME RAPPELLE QUELQU'UN",
"C'EST UN SQUELETTE OSSEUX",
- "REGARDE! MIGUEL BOS\220!",
- "IL DORME. CE SERAIT DOMMAGE LE R\220VEILLER",
- "IL EST PLUS MOCHE QU'\220MILE DE PAZ",
+ "REGARDE! MIGUEL BOSE!",
+ "IL DORT. CE SERAIT DOMMAGE DE LE R\220VEILLER",
+ "IL EST PLUS MOCHE QU'EMILIO DE PAZ",
// 450
"UN CERCUEIL EN BOIS DE PIN",
"IL VA ME COUPER EN PETITES TRANCHES, COMME UN SAUCISSON",
"JE N'AIME PAS LES PENDULES. JE PR\220F\324RE LES ARTICHAUTS",
- "MES MAINS SONT EMMENOTT\220ES. JE N'ARRIVERAI PAS",
- "IL SAUTE AUX YEUX QUE C'EST UNE PORTE SECR\324TE",
+ "MES MAINS SONT MENOTT\220ES. JE N'Y ARRIVE PAS",
+ "\200A SAUTE AUX YEUX QUE C'EST UNE PORTE SECR\324TE",
// 455
"ILS M'IGNORENT",
- "C'EST BIEN!",
- "DANS LE SCRIPT IL BOUGEAIT, MAIS LE JEU A SURPASS\220 LE BUDGET ET ON N'A PAS PU ME PAYER UN GYMNASE POUR ME METTRE EN FORME. DONC, POINT DU TOUT",
- "ELLE PARA\327T UN PEU D\220TACH\220E DU MUR",
- "JE NE CROIS PAS POUVOIR M'EN SERVIR. ELLE TROP HUMIDE POUR L'ALLUMER",
+ "ALLEZ..!",
+ "DANS LE SCRIPT C'\200TAIT SUPPOS\202 BOUGER, MAIS LE BUDGET DU JEU A EXPLOS\202 ET ILS N'ONT PAS PU ME PAYER LA GYM. DONC JE SUIS TOUJOURS MINABLE. FIN DE L'HISTOIRE",
+ "\200A PARA\327T MAL FIX\220 AU MUR",
+ "JE NE CROIS PAS QUE \200A VA M'AIDER. C'EST TROP HUMIDE POUR BR\352LER",
// 460
- "\267 L'AILE OUEST? -M\322ME PAS EN FOU ACHEV\220! -VA SAVOIR QU'AURAIT-IL L\267-BASI!",
- "IL Y A DE JOLIS MOTIFS TRANSYLVANS",
+ "VERS L'AILE OUEST? PAS QUESTION! PERSONNE NE SAIT CE QU'IL Y A L\267-BAS!",
+ "ELLE A DE JOLIS MOTIFS TRANSYLVANIENS",
"",
- "QUEL DOMMAGE NE PAS AVOIR TROUV\220 L\267-DEDANS UN PETIT AGNEAU EN TRAIN DE SE R\342TIR!",
- "LA DERNI\324RE FOIS QUE J'AI OUVERT UN FOURNEAU LA MAISON A VOL\220 EN \220CLATS",
+ "QUEL DOMMAGE QU'IL N'Y AI PAS UN PETIT AGNEAU EN TRAIN DE R\342TIR L\267-DEDANS!",
+ "LA DERNI\324RE FOIS QUE J'AI OUVERT UN FOURNEAU J'AI EXPLOS\220 LA MAISON",
// 465
- "C'EST L'ENSEIGNE DE L'\220QUIPE DE FOOT-BALL DE LA TRANSYLVANIE",
- "ET POURQUOI FAIRE? POUR ME LA METTRE \267 LA T\322TE?",
- "JE NE CROIS PAS QUE CES TIROIRS SOIENT DE CEUX QUI S'OUVRENT",
- "JE NE VEUX PAS SAVOIR LA NOURRITURE QU'IL Y AURA L\267-DEDANS!",
+ "C'EST L'ENSEIGNE DE L'\220QUIPE DE FOOTBALL DE LA TRANSYLVANIE",
+ "POURQUOI FAIRE? POUR LE METTRE SUR MA T\322TE?",
+ "JE NE CROIS PAS QUE CES TIROIRS SOIENT DU GENRE QUI S'OUVRENT",
+ "JE NE VEUX PAS SAVOIR QUEL TYPE DE NOURRITURE IL Y A L\267-DEDANS!",
"J'AI L'IMPRESSION QUE C'EST DE L'IMPRESSIONNISME",
// 470
- "LA NUIT S'EMPARE DE NOUS TOUS... QUELLE PEUR, N'EST-CE PAS?",
- "ELLE EST BOUCH\220E",
- "C'EST LE ROI. NE L'AVAIT-TU PAS IMAGIN\220?",
- "NON, J'EN AI D\220J\267 UN CHEZ MOI ET JE LUI DONNE \267 MANGER EN PLUS",
- "UN PLACARD AVEC DES LIVRES ET D'AUTRES CHOSES",
+ "LA NUIT TOMBE SUR NOUS TOUS... C'EST \202FRAYANT, N'EST-CE PAS?",
+ "C'EST COINC\220",
+ "C'EST ELVIS LE ROI. TU NE PENSAIS PAS LE VOIR ICI N'EST-CE PAS?",
+ "NON, J'EN AI D\220J\267 UN CHEZ MOI \205 NOURRIR",
+ "UNE \202TAG\324RE AVEC DES LIVRES ET D'AUTRES CHOSES",
// 475
- "ET QUI J'APPELLE \267 CES HEURES-L\267?",
+ "ET QUI PUIS-JE APPELLER \267 CES HEURES-L\267?",
"\"COMMENT FAIRE LA D\220CLARATION D'IMP\342TS\" COMME C'EST INT\220RESSANT!",
- "J'AI D\324J\267 UN CHEZ MOI. JE CROIS QUE C'EST UN BEST-SELLER MONDIAL",
+ "J'EN AI D\324J\267 UN CHEZ MOI. JE CROIS QUE C'EST UN BEST-SELLER MONDIAL",
"UNE CL\220 COMPL\324TEMENT NORMALE",
- "IL ME SEMBLE QUE CELLE-CI N'EST PAS D'ICI",
+ "JE CROIS QU'ELLE N'EST PAS DU COIN",
// 480
- "H\220! CE SONT DES FRITES SOUS FORME DE DENT CANINE! \200A ME PLA\327T",
- "JE NE CROIS PAS QU'IL SOIT LE MEILLEUR MOMENT POUR MANGER DES GOURMANDISES, AVEC MA FIANC\220E AUX MAINS DE L'\322TRE LE PLUS MAUVAIS QU'UNE M\324RE A PU ACCOUCH\220",
- "COMME JE SUIS BIEN EN TUANT DES VAMPIRES AVEC CELA!",
- "VOYONS SI APPARA\327T T\342T UN AUTRE",
- "NON, IL FAUT QU'IL SOIT AVEC UN SALE ET PUANT VAMPIRE, COMME CELUI QUI J'AI TU\220 AVANT",
+ "H\220! CE SONT DES FRITES EN FORME DE CROCS! J'ADORE",
+ "JE NE CROIS PAS QUE CE SOIT LE MEILLEUR MOMENT POUR MANGER CETTE MERDE, AVEC MA FIANC\220E AUX MAINS DE L'\322TRE LE PLUS MAUVAIS DE LA GALAXIE",
+ "COMME JE M'AMUSE \205 TUER DES VAMPIRES AVEC CE TRUC!",
+ "VOYONS SI UN AUTRE APPARA\327T BIENT\342T",
+ "NON, IL FAUT QUE CE SOIT AVEC UN SALE ET PUANT VAMPIRE, COMME CELUI QUI J'AI TU\220 AVANT",
// 485
- "C'EST L'AUTHENTIQUE PERRUQUE QU'ELVIS AVAIT UTILIS\220E QUAND IL EST DEVENU CHAUVE",
- "C'EST DE LA FARINE, MAIS JE NE PEUX PAS DIRE DES MARQUES",
- "PEUT-\322TRE DANS UN AUTRE MOMENT, D'ACCORD?",
- "C'EST UNE HACHE MAGNIFIQUE, DOMMAGE DE NE PAS POUVOIR SE PAYER AUCUNE T\322TE DE VAMPIRE PAR L\267",
+ "C'EST L'AUTHENTIQUE PERRUQUE QU'ELVIS A UTILIS\220E QUAND IL EST DEVENU CHAUVE",
+ "C'EST DE LA FARINE, MAIS JE NE PEUX PAS DIRE DE MARQUES",
+ "PEUT-\322TRE UNE AUTRE FOIS, D'ACCORD?",
+ "C'EST UNE HACHE MAGNIFIQUE, DOMMAGE QU'IL N'Y AI PAS UNE T\322TE DE VAMPIRE DANS LE COIN",
"NON. JE SUIS UNE BONNE PERSONNE AU FOND",
// 490
- "C'EST LE D\220ODORANT DE LA THACHER-HI!HI!HI!",
- "C'EST UNE CAPE ASSEZ MIGNONE",
+ "C'EST LE D\220ODORANT DE MARGARET THACHER...HI!HI!HI!",
+ "C'EST UNE CAPE ASSEZ SYMPA",
"",
- "TOUT COMME LES BRANCHES DE TOUS LES ARBRES DU MONDE, C'EST-\267-DIRE SANS RIEN DE PARTICULIER",
- "OH! C'EST INCROYABLE! -UNE CORDE DANS UNE AVENTURE DESSIN\220E!",
+ "COMME TOUTES LES BRANCHES DE TOUS LES ARBRES DU MONDE, ELLE N'A RIEN DE PARTICULIER",
+ "OH! C'EST INCROYABLE! UNE CORDE DANS UN JEU D'AVENTURE!",
// 495
- "JE ME DEMANDE \267 QUOI SERVIRA-T-ELLE...?",
+ "JE ME DEMANDE \267 QUOI ELLE VA SERVIR...?",
"UNE CORDE ATTACH\220E \267 UNE BRANCE OU UNE BRANCHE ACROCH\220E \267 UNE CORDE, \200A D\220PEND DU POINT DE VUE",
- "IL PARA\327T QUE CETTE PIE \267 DE TR\324S MAUVAISES INTENTIONS",
- "TAIS-TOI! JE NE LA DIS RIEN, POUR SI ELLE SE F\266CHE",
- "ELLE SEMBLE MORTE, MAIS C'EST UNE MENSONGE",
+ "CETTE PIE SEMBLE AVOIR DE TR\324S MAUVAISES INTENTIONS",
+ "OUBLIE \200A! JE ME TAIS SINON IL VA ENCORE SE F\266CHE",
+ "ELLE SEMBLE MORTE, MAIS C'EST POUR DE FAUX.",
// 500
- "IL N'Y A AUCUN ANIMAL ABiM\220 DANS LA PRODUCTION DE CE JEU",
+ "AUCUN ANIMAL N'A \202T\202 ABIM\220 DANS LA PRODUCTION DE CE JEU",
},
{
// 0
@@ -4220,52 +4220,52 @@ const char *_textd[NUM_LANGS][NUM_TEXTD] = {
{
// 0
"",
- "COMMENT VA TOUT, IGOR?",
- "C'EST TOUJOURS LA M\322ME CHOSE QUAND IL Y A UN BON MATCH \267 LA PARABOLIQUE! ENFIN, ALLONS LE VOIR AU BAR, COMME D'HABITUDE",
- "MAINTENANT, COUTE IGOR. NOUS ALLONS RALISER LA PHASE 1 DE MON PLAN POUR CONQURIR LE MONDE",
- "D'ABORD, ON SAISIRA L'UNE DES FOUDRES DE L'ORAGE ET ON LA DMAGNTISERAIT AVEC l'INDIFUBULATEUR. LA COURANTE PASSERA DANS MON MONSTRE ET LUI DONNERA LA VIE!",
+ "H\220 IGOR, COMMENT \200A AVANCE?",
+ "C'EST TOUJOURS LA M\322ME CHOSE QUAND IL Y A UN BON MATCH \267 LA T\220L\220! ENFIN, NOUS IRONS LE VOIR AU BAR, COMME D'HABITUDE",
+ "\202COUTE ATTENTIVEMENT IGOR. NOUS ALLONS COMMENCER LA PHASE 1 DE MON PLAN POUR CONQU\220RIR LE MONDE",
+ "D'ABORD, ON ON VA CAPTER LA FOUDRE PUIS ON LA D\220MAGN\220TISERA AVEC l'INDIFIBULATEUR. L'\202LECTRICIT\220 PASSERA DANS MON MONSTRE ET LUI DONNERA VIE!",
// 5
- "SI TOUT VA BIEN CELUI-L\267 NE SERA QUE LE PREMIER D'UNE IMMENSE ARME QUE CONQURRA LE MONDE POUR MOI, HA! HA! HA! ",
- "LES MONSTRES VONT ANANTIR TOUTES LES ARMES DE TOUTES LES ARMES DU MONDE, TANDIS QUE NOUS NOUS RFUGIONS DANS LES TERRAINS QUE J'AI ACHET \267 GIBRALTAR",
- "ALORS, ON DONNERA UN COUP D'TAT. LES GOUVERNEMENTS DU MONDE N'AURONT PAS DE PROTECTION ET SES PAYS SERONT \267 MES PIEDS",
- "JE SERAI LE PREMIER MALIN DE L'HISTOIRE \267 Y AVOIR RUSSI! -HOUA! HOUA! HOUA!",
- "POUR TOI RIEN, IDIOT! JE PRSENTE LA TRAME. BIEN, TOUT EST PR\322T? ",
+ "SI TOUT VA BIEN CELUI-L\267 NE SERA QUE LE PREMIER D'UNE IMMENSE ARM\220E QUI CONQUERRA LE MONDE, HA! HA! HA! ",
+ "MES MONSTRES VONT AN\220ANTIR TOUTES LES ARMES DE TOUTES LES ARM\220ES DU MONDE, TANDIS QUE NOUS NOUS R\220FUGIRONS DANS LE TERRAIN QUE J'AI ACQUIS \267 GIBRALTAR",
+ "ALORS, ON FERA UN COUP D'\202TAT. LES GOUVERNEMENTS DU MONDE N'AURONT PLUS DE PROTECTION ET SE PROSTERNERONT \267 MES PIEDS",
+ "JE SERAI LE PREMIER M\220CHANT DE L'HISTOIRE \267 Y AVOIR R\220USSI! HOUA! HOUA! HOUA!",
+ "JE NE TE PARLE PAS, IDIOT! JE PR\220SENTE JUSTE LA TRAME. BIEN, TOUT EST PR\322T? ",
// 10
- "LE MOMENT EST ARRIV\220, ALORS! -APPUIE DONC SUR L'INTERRUPTEUR DES BATTERIES ALCALINES! ",
- "H\220LAS! QU'EST-CE QUE N'A PAS MARCH\220?",
- "C'EST S\352R QUE TU L'AS BIEN R\220VIS\220 ET IL NE MANQUAIT RIEN? DERNI\324REMENT AVEC CETTE HISTOIRE DE LA RENTE TU N'Y VOIS GOUTTE",
- "IDIOT! TU N'AVAIS PAS CONNECT\220 L'INFIBULATEUR! LES VIS SE SERONT MAGN\220TIS\220ES ET SA CERVELLE AURA BR\352L\220E",
+ "LE MOMENT EST VENU! APPUIE DONC SUR L'INTERRUPTEUR DES BATTERIES ALCALINES! ",
+ "ZUT! QU'EST-CE QUI N'A PAS MARCH\220?",
+ "TU ES S\352R D'AVOIR TOUT V\220RIFI\220 ET QU'IL NE MANQUAIT RIEN? DERNI\324REMENT TU AS TRAFICOT\220 AVEC CE TRUC DE TAXE ET JE NE SAIS PAS...",
+ "IDIOT! TU AVAIS OUBLI\220 DE CONNECTER L'INDIFIBULATEUR! LES VIS SE SERONT MAGN\220TIS\220ES ET SA CERVELLE AURA BR\352L\220E",
"TU ES MORT, TU ES MORT, SI JE T'ATTRAPE...",
// 15
- "TAIS-TOI! DEMAIN J'AURAI UNE AUTRE CERVELLE ET ON FERA L'ESSAI \267 NOUVEAU",
- "NON, CETTE FOIS J'APPORTERAI UNE DE FEMME POUR QU'ELLE SOIT TOUTE NEUVE ET DE PREMI\324RE MAIN. HA! HA! HA! QUE MALIN DE XISTE",
- "ET QUOI? JE SUIS LE MALIN ET TOUT LE SEXISTE QUE JE D\220SIRE, C'EST ENTENDU? SI TU ME R\220PLIQUES ENCORE JE TE FAIS ENGLOUTIR TA BOSSE",
- "HA! HA! HA!. UN AUTRE QUI S'EST LAISS\220 PRENDRE. MAINTENANT TU PAIERAS CHER TON AUDACE DE VOULOIR EN FINIR AVEC MOI. -IGOR, AU PENDULE DE LA MORT!",
- "DIS-MOI, HUMAIN STUPIDE, POURQUOI TU AS PENS\220 ME D\220TRUIRE? ",
+ "TAIS-TOI! DEMAIN JE R\220CUP\220RERAI UN AUTRE CERVEAU ET ON RECOMENCERA L'EXP\220RIENCE",
+ "CETTE FOIS J'UTILISERAI UN CERVEAU DE FEMME BRILLANT ET ENCORE INUTILIS\220. HA! HA! HA! ELLE EST BONNE!",
+ "ET QUOI? JE SUIS LE M\220CHANT NON? JE PEUX \210TRE AUSSI SEXISTE QUE JE VEUX. ET SI TU ME R\220POND ENCORE UNE FOIS JE TE FAIS AVALER TA BOSSE",
+ "HA! HA! HA!. TU T'ES FAIT AVOIR!! MAINTENANT TU VA PAYER POUR AVOIR OS\220 T'EN PRENDRE \205 MOI. IGOR, AU PENDULE DE LA MORT!",
+ "DIS-MOI, HUMAIN STUPIDE, POURQUOI VEUX-TU ME D\220TRUIRE? ",
// 20
- "QUE C'EST BEAU! JE SANGLOTERAIS SI CE N'ETAIT PAS DR\342LE",
- "J'AI BESOIN DE TA FIANC\220E POUR QU'ELLE M'AIDE \267 CONQU\220RIR LE MONDE AVEC SA CERVELLE",
- "OUI, HA! JE LA LUI ARRACHERAI ET L'INSTALLERAI SUR MON FRUSKYNSTEIN, ET AVEC LUI JE VAIS MA\327TRISER LE MONDE, HA! HA! HA!",
- "QUOI?! -TU ES MORT, TU ES MORT! JE VAIS TE... TU M'AS CASS\220 LE NEZ, ALLONS, -APPR\322TE-TOI \267 \322TRE TU\220!",
+ "QUE C'EST BEAU! J'EN PLEURERAI SI \200A NE ME FAISAIT PAS RIRE",
+ "J'AI L'INTENTION D'UTILISER LE CERVEAU DE TA FIANC\220E POUR M'AIDER \205 CONQU\220RIR LE MONDE",
+ "OUI! JE LE LUI ARRACHERAI ET LE METTRAI DANS MON FRUSKYNSTEIN. AVEC LUI LE MONDE SERA \205 MOI, HA! HA! HA!",
+ "QUOI?! TU ES MORT! JE VAIS TE... TU M'AS MIS EN COL\324RE... ALLEZ, PR\220PARE-TOI \267 MOURIR!",
"HA! HA! HA! C'EST-CE QUE TU CROIS",
// 25
"OUI, N'EST-CE PAS? HA! HA! HA!",
- "AH, C'EST BIEN! TU PEUX FUMER LA DERNI\324RE CIGARETTE, MAIS D\220P\322CHE-TOI!",
- "\220TEINS D\220J\267 CETTE CIGARETTE, J'EN A RAS LE BOL!",
- "ET DIS-MOIS, CETTE POTION, A-T-ELLE L'EFFET CONTRAIRE?",
+ "OK, OK! MAIS D\220P\322CHE-TOI DE LA FUMER!",
+ "\220TEINS CETTE CIGARETTE MAINTENANT, J'EN AI RAS LE BOL!",
+ "ET DIS-MOI, CETTE POTION IMUNISE-T-ELLE AUSSI LES VAMPIRES??",
"ON VERRA \200A...",
// 30
- "EH BIEN, ON VERRA SI C'EST VRAI. IGOR, APPORTE LE COMPACT DISC D'ONGLES GRATTANT UN TABLEAU",
- "N'Y PENSES PAS. LA FILLE RESTE AVEC MOI, ET TOI TU RESTERAS L\267 JUSQU'\267 QUE LA PENDULE TE COUPE EN PETITES TRANCHES. HA! HA! HA!",
- "MAIS QUE JE SUIS MALIN. ON Y VA, IGOR, ALLONS PR\220PARER LA POTION ET CONQU\220RIR LE MONDE",
+ "BIEN, ON VA VOIR. IGOR, APPORTE MOI LE CD \"ONGLES GRATTANT UN TABLEAU NOIR\"",
+ "PAS QUESTION. LA FILLE RESTE AVEC MOI, ET TOI TU RESTERAS ICI JUSQU'\267 CE QUE LE PENDULE TE COUPE EN PETITES TRANCHES. HA! HA! HA!",
+ "H\220 H\220, JE SUIS TELLEMENT M\220CHANT... VIENS IGOR, ALLONS PR\220PARER LA POTION ET CONQU\220RIR LE MONDE",
"QU'Y A-T-IL MAINTENANT?",
- "OUI, QU'Y A-T-IL?... -TIENS, LE MATCH!",
+ "OUI, QU'Y A-T-IL?... OH ZUT, LE MATCH!",
// 35
"JE L'AVAIS OUBLI\220. PRENDS LA FILLE ET ALLONS LE VOIR. J'IRAI CONQU\220RIR LE MONDE PLUS TARD",
"MERCI MON VIEUX, J'AVAIS SOIF",
- "ArgH! CE CRUCIFIX! -CE CRUCIFIX!...",
- "C'EST BEAU CE CRUCIFIX, JE NE M'AVAIS PAS RENDU COMPTE",
- "FICHE-MOI LA PAIX! JE REGARDE LE FOOT-BALL",
+ "ARGH! LE CRUCIFIX! ...LE CRUCIFIX...!",
+ "JE N'AVAIS PAS REMARQU\220 CE BEAU CRUCIFIX!",
+ "FICHE-MOI LA PAIX! JE REGARDE LE MATCH",
// 40
"",
"",
@@ -4286,39 +4286,39 @@ const char *_textd[NUM_LANGS][NUM_TEXTD] = {
"",
// 55
"",
- "BONJOUR, L'AVEUGLE. A VA?",
- "POURQUOI TU SAIS QUE JE SUIS UN TRANGER?",
- "TU PARAT UN AVEUGLE. TU AS DES LUNETTES COMME SERAFIN ZUBIRI ET TU PARLES EN REGARDANT DEVANT TOI, COMME STEVIE WONDER...",
- "BON, EXCUSE-MOI. JE NE SAVAIS PAS QUE TU PUISSES VOIR.",
+ "BONJOUR, L'AVEUGLE. \200A VA?",
+ "COMMENT TU SAIS QUE JE SUIS UN \202TRANGER?",
+ "TU PARA\214T AVEUGLE. TU AS DES LUNETTES NOIR COMME STEVIE WONDER...",
+ "BON, EXCUSE-MOI. JE NE SAVAIS PAS QUE TU POUVAIS VOIR.",
// 60
- "MAIS, TU NE VIENS PAS DE ME DIRE QUE TU N'EST PAS AVEUGLE?",
- "MAIS TU NE VOIS PAS!",
- "A VA, A VA.... PARDONNE-MOI. DANS CE CAS-L: BONJOUR AVEUGLE.",
- "JE SUIS JOHN HACQUER ET JE JOUE AU DRASCULA. TU ES SREMENT LE TYPIQUE PERSONNAGE QUI VA M'AIDER EN TROC D'UN OBJET. D'ACCORD? H? D'ACCORD?",
- "EUH... AVEUGLE, UNE QUESTION! MAIS... QUEL GENRE DE MTIER C'EST LE TIEN? CELUI D'CHANGER DE FAUCILLES CONTRE DE L'ARGENT EN JOUANT DE L'ACCORDON?",
+ "MAIS, TU VIENS DE ME DIRE QUE TU N'ES PAS AVEUGLE",
+ "MAIS SI TU NE VOIS PAS",
+ "OOOOOKAY, D\220SOL\220. DANS CE CAS: BONJOUR PERSONNE NON VOYANTE",
+ "JE SUIS JOHN HACKER ET JE JOUE \205 DRASCULA. TU ES S\352REMENT UN DE CES PERSONNAGES QUI VA M'AIDER EN \202CHANGE D'UN OBJET. HEIN? C'EST \200A, HEIN?",
+ "EUH EXCUSE MOI DE DEMANDER AVEU... PERSONNE NON VOYANTE, MAIS QUEL GENRE DE M\220TIER C'EST? DONNER DES FAUCILLES CONTRE DE L'ARGENT EN JOUANT DE L'ACCORD\220ON?",
// 65
- "AH, OUI! C'EST VRAI. AU REVOIR AVEUGLE...",
- "VOIL LA GROSSE SOMME D'ARGENT QUE TU M'AS DEMANDE.",
- "IL TE VAUX MIEUX.",
- "BONJOUR, TRANGER!",
- "ET TOI... COMMENT TU SAIS QUE JE SUIS UN AVEUGLE?",
+ "AH, OUI! JE SUPPOSE QUE C'EST VRAI. AU REVOIR PERSONNE NON VOYANTE... AVEUGLE",
+ "VOIL\267 LA GROSSE SOMME D'ARGENT QUE TU M'AS DEMAND\220.",
+ "IL VAUX MIEUX.",
+ "BONJOUR, \202TRANGER!",
+ "ET COMMENT TU SAIS QUE JE SUIS AVEUGLE?",
// 70
- "ET TOI TU PARLES COMME LE FILS DE BILL COSBY ET MOI, JE NE T'EMBTE PAS.",
- "NON, SI JE NE VOIS PAS.",
- "ET MOI, JE NE LE SUIS PAS.",
- "OH, BIEN SR! COMME JE NE VOIS PAS ON ME TCHE D'AVEUGLE, N'EST-CE PAS",
- "BONJOUR, TRANGER! ET QU'EST-CE QUE TU FAIS EN TRANSYLVANIE?",
+ "ET JE NE PLAISANTE PAS MAIS LES TIENNES RESSEMBLENT \205 CELLES DE WOODY ALLEN.",
+ "NON, JE NE VOIS PAS.",
+ "ET JE NE LE SUIS PAS.",
+ "OH, BIEN S\352R! JUSTE PACEQUE JE NE VOIS PAS TU M'ACCUSES D'\210TRE D'AVEUGLE",
+ "BONJOUR, \202TRANGER! ET QUE VIENS TU FAIRE EN TRANSYLVANIE?",
// 75
- "C'EST CORRECT, TRANGER. POUR UNE GROSSE SOMME D'ARGENT, JE TE DONNERAI UNE FAUCILLE, POUR QUAND TU EN AURAS BESOIN.",
+ "C'EST CORRECT, \202TRANGER. EN \202CHANGE D'UNE GROSSE SOMME D'ARGENT JE TE DONNERAI UNE FAUCILLE. ON NE SAIT JAMAIS QUAND ON PEUT EN AVOIR BESOIN.",
"CHUT! JE SUIS UN TRAFICANT DE FAUCILLES, JE DOIS ME CACHER.",
"PARCE QUE TU ME L'AS DIT AVANT, N'EST-CE PAS?",
- "MERCI TRANGER. VOIL TA FAUCILLE EN CHANGE. UNE CHOSE QUI SERA TRS PRATIQUE POUR TOI, PLUS TARD... VRAIMENT.",
+ "MERCI \202TRANGER. VOICI TA FAUCILLE EN \202CHANGE. TU VAS LA TROUVER TR\324S UTILE PLUS TARD... TU VERRAS.",
"",
// 80
"",
"",
- "No, nada",
- "bla, bla, bla."
+ "NON, NON, RIEN",
+ "BLA, BLA, BLA."
},
{
// 0
@@ -4489,22 +4489,22 @@ const char *_textb[NUM_LANGS][NUM_TEXTB] = {
{
// 0
"",
- "ICI, EN BUVANT",
+ "JE SUIS ICI, ET JE BOIS",
"TOUS MORTS. MERCI. BOURRP",
"OUI, VRAIMENT...",
- "CELLE-CI POUR L'ONCLE DSIR",
+ "CELLE-CI EST POUR L'ONCLE D\220SIR\220",
// 5
- "ET CELLE-L\267 POUR LE CADAVRE D'ONCLE DSIR",
- "MON ONCLE EST ALL AU CH\266TEAU ET N'EST PAS ENCORE REVENU",
- "BON, IL EST REVENU MAIS POUR PEU DE TEMPS. SI VON BRAUN N'AURAIT FAIT UN IMPAIR, MON ONCLE DSIR SERAIT ICI EN BUVANT",
+ "ET CELLE-L\267 POUR LE CADAVRE D'ONCLE D\220SIR\220",
+ "MON ONCLE EST ALL\220 AU CH\266TEAU ET N'EN EST JAMAIS REVENU",
+ "BON, IL EST REVENU JUSTE UN PETIT PEU. SI VON BRAUN N'AVAIT PAS FAIT UN IMPAIR, MON ONCLE D\220SIR\220 SERAIT ICI \267 BOIRE AVEC NOUS",
"RIEN... ",
- "EH OUI! CE MALIN NOUS A INTIMIDS \267 TOUS",
+ "EH OUI! CE MALIN NOUS A TOUS INTIMID\220S",
// 10
- "DE FOIS IL DESCEND SUR LE VILLAGE ET ENLEVE QUELQU'UN",
- "UN PEU PLUS TARD ON NE TROUVE QUE QUELQUES RESTES. JE PENSE QU'IL FAIT DU TRAFIQUE D'ORGANES, OU QUELQUE CHOSE PAREILLE",
- "LE SEUL DU VILLAGE QUI SAIT COMMENT FINIR AVEC DRASCULA IL A DES \220TUDES",
+ "DE TEMPS EN TEMPS IL DESCEND AU VILLAGE ET ENL\324VE QUELQU'UN",
+ "UN PEU PLUS TARD ON NE TROUVE QUE QUELQUES RESTES. JE PENSE QU'IL FAIT DU TRAFIQUE D'ORGANES, OU QUELQUE CHOSE COMME \200A",
+ "LE SEUL DU VILLAGE QUI SAIT COMMENT EN FINIR AVEC DRASCULA. IL A FAIT DES \220TUDES",
"DEPUIS QUE DRASCULA L'A VAINCU, IL S'EST RETIR\220 DANS UNE CABANE, EN DEHORS DU VILLAGE",
- "C'EST LE SEUL QUI POURRAIT NOUS AIDER \267 FINIR AVEC DRASCULA ET LUI NE VEUX RIEN SAVOIR DE NOUS. QU'EN PENSES-TUI?",
+ "C'EST LE SEUL QUI POURRAIT NOUS AIDER \267 EN FINIR AVEC DRASCULA ET LUI NE VEUX RIEN SAVOIR. QU'EN PENSES-TUI?",
},
{
// 0
@@ -4645,37 +4645,37 @@ const char *_textbj[NUM_LANGS][NUM_TEXTBJ] = {
"",
"VOUS ALLEZ BIEN? ALLEZ, REVEILLEZ-VOUS! VOUS M'ENTENDEZ? VOUS \322TES MORT?",
"NON, MON NOM EST BILLIE JEAN, MAIS TU PEUX M'APPELLER B.J., C'EST PLUS COURT",
- "HI! HI! -C'EST BON!",
- "EN VRIT JOHNY, J'TAIS L\267, PR\322TE \267 ME COUCHER, ET J'AI ENTENDU UN FORT COUP DANS LE COULOIR",
+ "HI! HI! ELLE EST BONNE!",
+ "EN FAIT JOHNNY, J'\220TAIS L\267, PR\322TE \267 ME COUCHER, ET J'AI ENTENDU UN BRUIT DANS LE COULOIR",
// 5
- "AU DBUT JE N'Y AI PAS DONN D'IMPORTANCE, MAIS APR\324S JE ME SUIS RENDUE COMPTE QUE NE POUVAIS PAS M'ENDORMIR ET SUIS SORTIE FAIRE UNE PROMENADE",
- "ET \267 MON GRAN TONNEMENT, QUAND J'AI OUVERT LA PORTE, JE T'AI TROUV L\267, PAR TERRE. J'TAIS SUR LE POINT DE PENSER QUE TU AVAIS MORT, HE!, QUE JE SUIS B\322TE!",
- "J'ALLAIS TE FAIRE LE BOUCHE-\267-BOUCHE MAIS IL N'A T PAS NCESSAIRE PUISQUE TU AS COMMENC \267 PARLER",
- "TU DISAIS \267 SAVOIR QUOI D'UN POUVANTAIL. CELA M'A FAIT UNE GRANDE PEUR, PARCE QUE QUAND UN MORT SE MET \267 PARLER L'IMPRESSION EST TR\324S FORTE, NE CROIS-TU PAS?",
- "C'EST VRAI, NON? ENFIN. JE ME SUIS DBROUILLE POUR TE PRENDRE SUR MOI, T'EMMENER DANS MA CHAMBRE ET T'INSTALLER SUR LE LIT... ET VOIL\267 TOUT. HI! HI! HI!",
+ "AU D\220BUT JE N'Y AI PAS PR\322T\220 ATTENTION, MAIS COMME JE N'ARRIVAIS PAS \267 DORMIR JE SUIS SORTIE DANS LE COULOIR",
+ "ET \267 MON GRAND \220TONNEMENT, QUAND J'AI OUVERT LA PORTE, JE T'AI TROUV\220 L\267, PAR TERRE. J'AI CRU QUE TU \220TAIS MORT!, QUE JE SUIS B\322TE!",
+ "J'ALLAIS TE FAIRE DU BOUCHE-\267-BOUCHE MAIS IL CELA N'A PAS \220T\220 N\220CESSAIRE PUISQUE TU AS COMMENC\220 \267 PARLER",
+ "TU PARLAIS D'UN \220POUVANTAIL. J'AVAIS TR\324S PEUR, TU SAIS. \200A FAIT UN CHOC QUAND UN MORT SE MET \267 PARLER",
+ "C'EST VRAI, NON? ENFIN, JE ME SUIS D\220BROUILL\220E POUR T'EMMENER DANS MA CHAMBRE ET T'INSTALLER SUR LE LIT... ET VOIL\267, C'EST TOUT. HI! HI! HI!",
// 10
- "HO! CE N'EST PAS \267 CAUSE DU COUP, HI! HI! CE QUE J'AI MARCHE SUR TES LUNETTES PAR ACCIDENT",
- "\220VIDEMMENT LES LUNETTES LUI FONT DU BIEN! JE SAIS BIEN QUIL N'EST PAS FERNANDO LANCHA, MAIS IL A UN AIR QU'ALLEZ DONC SAVOIR",
- "OUI, OUI, JE VEUX... VAS-Y, EMBRASSE-MOI FORT, EMBRASSE-MOI BEAUCOUP...",
- "OH,JOHNY!, MON AMOUR! HEUREUSEMENT QUE TU ES VENU. CE M\220CHANT DRASCULA M'A ATTACH\220 AU LIT ET APR\324S IL EST PARTI EN BAS VOIR LE MATCH",
+ "HO, NON! CE N'EST PAS \267 CAUSE DU COUP, HI! HI! C'EST PARCE QUE J'AI MARCH\220 SUR TES LUNETTES PAR ACCIDENT",
+ "CES LUNETTES LUI VONT VRAIMENT BIEN! JE SAIS BIEN QU'IL N'EST PAS BRAD PITT, MAIS IL EST ATTIRANT...",
+ "OUI, OUI, JE VEUX... VAS-Y, SERRE-MOI, EMBRASSE-MOI PASSIONN\220MANT...",
+ "OH, JOHNNY!, MON AMOUR! HEUREUSEMENT QUE TU ES L\267. CE M\220CHANT DRASCULA M'A ATTACH\220 AU LIT ET APR\324S IL EST PARTI EN BAS VOIR LE MATCH",
"OUI, C'EST VRAI, LIB\324RE-MOI",
// 15
- "NON, SUIS D\220SOL\220E. JE LES AI TOUTES UTILIS\220ES, DANS LE CACHOT, EN T\266CHANT DE ME LIB\220RER ALORS QUE TOI TU M'ABANDONNAIS",
- "JOHNY, C'EST TOI? -QUELLE JOIE! -JE SAVAIS QUE TU VIENDRAIS!",
- "TU NE PEUX PAS SAVOIR COMBIEN M'A FAIT SOUFFRIR CE M\220CHANT DRASCULA",
- "D'ABORD IL M'EMMEN\220 EN VOLANT JUSQU'ICI ET APR\324S IL M'ENFERM\220E DANS CE TAUDIS, SANS MIROIR NI RIEN D'AUTRE",
- "COMME TU L'ENTENDS. ET LE PIRE C'EST QUIL NE M'A PAS DEMAND\220 PARDON N'UNE SEULE FOIS ",
+ "NON, JE SUIS D\220SOL\220E. JE LES AI TOUTES UTILIS\220ES, DANS LE CACHOT, EN ESSAYANT DE ME LIB\220RER ALORS QUE TOI TU M'ABANDONNAIS",
+ "JOHNNY, C'EST TOI? QUEL SOULAGEMENT! JE SAVAIS QUE TU VIENDRAIS!",
+ "TU NE PEUX PAS SAVOIR COMBIEN CE M\220CHANT DRASCULA M'A FAIT SOUFFRIR",
+ "D'ABORD IL M'A EMMEN\220 EN VOLANT JUSQU'ICI ET ENSUITE IL M'A ENFERM\220E DANS CE TAUDIS, SANS MIROIR NI RIEN D'AUTRE",
+ "JE TE LE DIS. ET LE PIRE C'EST QUIL NE S'EST M\322ME PAS EXCUS\220 UNE SEULE FOIS",
// 20
- "JOHNY, MON CH\220RI, O\353 EST-CE QUE TU ES?",
- "JE SUIS PR\322TE POUR QUE TU M'EN SORTES D'ICI",
+ "JOHNNY CH\220RI, O\353 ES-TU?",
+ "JE SUIS PR\322TE \267 PARTIR CH\220RI",
"ATTEND, JE VAIS REGARDER... NON CH\220RI, JE REGRETTE",
"TIENS...",
- "\"CHER JOHNY",
+ "\"CHER JOHNNY",
// 25
- "JAMAIS JE T'OUBLIERAI, MAIS JE ME SUIS APER\200UE QUE NOTRE AFFAIRE NE POUVAIT PAS MARCHER. JE VAIS \322TRE SINC\324RE AVEC TOI: IL Y A UN AUTRE HOMME, PLUS GRAND...",
- "ET ENCORE IL M'A LIB\220R\220 DES MAINS DE DRASCULA. IL M'A DEMAND\220 EN MARIAGE, ET MOI J'AI ACCEPT\220",
- "AU REVOIR, JOHNY. N'Y CHERCHES PAS UNE EXPLICATION, L'AMOUR EST AVEUGLE ET N'\220COUTE PAS DES RAISONS",
- "J'ESP\324RE QUE TU SERAS SANS RANCUNE, ET N'OUBLIES PAS QUE JE T'AIME ENCORE, MAIS EN AMI SEULEMENT\"",
+ "JE NE T'OUBLIERAI JAMAIS, MAIS J'AI R\220ALIS\220 QUE \200A NE POUVAIT PAS MARCHER ENTRE NOUS. EN FAIT, IL Y A UN AUTRE HOMME, PLUS GRAND, PLUS FORT...",
+ "ET IL M'A LIB\220R\220 DES MAINS DE DRASCULA. IL M'A DEMAND\220 EN MARIAGE, ET J'AI ACCEPT\220",
+ "AU REVOIR, JOHNNY. NE CHERCHES PAS D'EXPLICATION, L'AMOUR EST AVEUGLE ET N'\220COUTE PAS LA RAISON",
+ "J'ESP\324RE QUE TU NE M'EN VOUDRAS PAS. SOUVIENS-TOI QUE JE T'AIME ENCORE, MAIS EN AMI SEULEMENT\"",
},
{
// 0
@@ -4815,32 +4815,32 @@ const char *_texte[NUM_LANGS][NUM_TEXTE] = {
// 0
"",
"OH\220! VOUS!",
- "QUEL MORT NI QUELLE HISTOIRE!",
- "JE SUIS VIVANT. C'EST QUE J'AI FAIM, VOUS SAVEZ? ",
- "J'TAIS L'IVROGNE DU VILLAGE, LE DIGNE REPRSENTANT D'UN FAMILLE D'ILLUSTRES SO\352LARDS, ET DRASCULA M'A SQUESTR UNE NUIT POUR ME VOLER LES ORGANES",
+ "ARR\322TE TES BLAGUES D\220BILES SUR LES MORTS, OK?",
+ "JE SUIS VIVANT. C'EST JUSQUE QUE JE SUIS AFFAM\220",
+ "J'\220TAIS L'IVROGNE DU VILLAGE, LE DIGNE REPR\220SENTANT D'UNE FAMILLE D'ILLUSTRES SO\352LARDS, ET DRASCULA M'A KIDNAPP\220 UNE NUIT POUR ME VOLER MES ORGANES",
// 5
- "COMME JE ME CONSERVE EN ALCOOL, IL ME TIENS ICI EN PLAN DBALLAGE. \267 CHAQUE FOIS QU'IL A BESOIN DE QUELQUE CHOSE POUR SON MONSTRE, IL LE PRENDRE",
- "AU DBUT A ME FAISAIT MAL, MAIS JE M'EN FICHE DJ\267",
- "JE NE SAIS PAS, MAIS CE SERA SON PROJET FIN D'TUDES",
- "MON NOM EST DSIR, POUR VOUS SERVIR",
- "VRAIMENT JE N'AI PAS UNE ENVIE FOLLE, MAIS MERCI QUAND M\322ME, MONSIEUR",
+ "COMME L'ALCOOL ME CONSERVE, IL ME GARDE ICI COMME DISTRIBUTEUR DE PI\324CES DE RECHANGES. CHAQUE FOIS QU'IL A BESOIN DE QUELQUE CHOSE POUR SON MONSTRE, IL SE SERT",
+ "\200A ME FAISAIT MAL AU D\220BUT, MAIS MAINTENANT JE M'EN FOUS",
+ "JE NE SAIS PAS, JE SUPPOSE QUE C'EST SON PROJET DE FIN D'\220TUDES",
+ "MON NOM EST D\220SIR\220, POUR VOUS SERVIR",
+ "EN FAIT JE N'EN AI PAS UNE ENVIE FOLLE, MAIS MERCI QUAND M\322ME, MONSIEUR",
// 10
- "OUI, TOI M\322ME",
- "POURQUOI TOUS LES JEUX D'AVENTURES FINISSENT AVEC UNE AUBE OU UN COUCHER DU SOLEIL? ",
- "ET VOIL\267 LES NOMS DE TOUS QUI ON FAIT LE JEU?",
- "ET N'ONT-ILS PAS LA HONTE DE SE MONTRER ET QUE TOUT LE MONDE LES VOIT?",
- "AH ZUT! IL NE FAIT QUE SORTIR \"EMILIO DE PAZ\" ",
+ "PRENDS LE, C'EST \267 TOI MAINTENANT",
+ "POURQUOI TOUS LES JEUX D'AVENTURES FINISSENT-ILS AVEC UN LEVER OU UN COUCHER DE SOLEIL?",
+ "TOUS CES NOMS SONT-ILS CEUX DES CR\220ATEURS DE CE JEU?",
+ "ET N'ONT-ILS PAS LA HONTE QUE TOUT LE MONDE LES VOIT?",
+ "MON DIEU! CE \"EMILIO DE PAZ\" EST PARTOUT",
// 15
- "C'EST VRAI",
+ "VRAIMENT?",
"OUI",
"EH BIEN, IL NE FAUT PAS EXAG\220RER",
- "EN FAIT HOMME-LOUP...",
- "...N'EST-TU PAS TOMB\220 D'UNE FEN\322TRE EN TE BR\220SILLANT?",
+ "EN FAIT LOUP-GAROU...",
+ "...N'ES-TU PAS TOMB\220 D'UNE FEN\322TRE ET BLESS\220 S\220RIEUSEMENT?",
// 20
- "SI DU MOINS N'\220TAIENT PAS TOUJOURS LES M\322MES...",
+ "AU MOINS CE N'\220TAIENT PAS TOUJOURS LES M\322MES...",
"CELUI-L\267 EST D\220J\267 SORTI QUATRE FOIS",
"J'AIMERAIS \322TRE MANNEQUIN",
- "PARFAITEMENT. ET TOI, QUE VAS-TU FAIRE?",
+ "D'ACCORD. ET TOI, QUE VAS-TU FAIRE?",
},
{
// 0
@@ -5006,44 +5006,44 @@ const char *_texti[NUM_LANGS][NUM_TEXTI] = {
{
// 0
"",
- "MA\327TRE, JE CROIS QUE \200A NE ROULE PAS",
- "J'EN SUIS TR\324S S\352R, MA\327TRE...",
- "JE LE REGRETTE, MA\327TRE",
- "IL APPORTERA UN AUTRE DE SCIENTIFIQUE FOU? JE VOUS PRVIENS QUE LE LABORATOIRE EN EST PLEIN ET TOUS SONT PRIMS",
+ "MA\327TRE, \200A NE MARCHE PAS",
+ "J'EN SUIS S\352R, MA\327TRE...",
+ "JE SUIS D\220SOL\220, MA\327TRE",
+ "ALLER VOUS RAPPORTER UN AUTRE SCIENTIFIQUE FOU? LE LABORATOIRE EN EST D\220J\267 PLEIN ET DE PLUS ILS SONT TOUS P\220RIM\220S",
// 5
- "TAISEZ-VOUS, MA\327TRE, SI LES FMINISTES VOUS COUTENT...",
- "CE QU'IL FAUT SUPPORTER!",
- "MA\327TRE! -JE NE VOUS ATTENDAIT PAS SI T\342T!",
- "A VA MAL MA\327TRE. IL DOIT AVOIR DES PROBL\324MES AVEC LE SATELLITE ET JE NE RUSSIT PAS \267 SINTONISER L'IMAGE. ET ENCORE L'ORAGE PRODUIT DES INTERFRENCES.",
- "CANCANS QUE TOUT CELA, MA\327TRE!",
+ "PLUS BAS, MA\327TRE, LES F\220MINISTES POURRAIENT VOUS ENTENDRE...",
+ "SAPRISTI!",
+ "JE NE VOUS ATTENDAIS PAS DE SIT\342T MA\327TRE!",
+ "\200A VA MAL MA\327TRE. IL Y A DES PROBL\324MES AVEC LE SATELLITE ET JE NE CAPTE PAS L'IMAGE. Il DOIT Y AVOIR DES INTERF\220RENCES AVEC L'ORAGE",
+ "QU'EN SAIS-JE, MA\327TRE?",
// 10
"OUI, MA\327TRE",
"MA\327TRE",
- "QUELLE HEURE IL EST?",
- "H\220? -AH! TU M'AS FAIT PEUR! TU EST CELUI DU \"NETTOYAGE DE NUIT\", NON?",
- "JE SUIS IGOR, LE MAJORDOME. TU PEUX COMMENCER PAR LE SALON DE BAL. HIER IL Y A EU UNE ORGIE SURNATURELLE ET C'EST UNE SALOPERIE",
+ "SAVEZ-VOUS L'HEURE QU'IL EST?",
+ "QUOI? -AH! TU M'AS FAIT PEUR! TU ES LE GARS DU NETTOYAGE, NON?",
+ "JE SUIS IGOR, LE MAJORDOME. TU PEUX COMMENCER PAR LA SALLE DE BAL. IL Y A EU UNE ORGIE SURNATURELLE HIER ET \200A SE VOIT",
// 15
- "SI TU AS BESOIN DE QUELQUE CHOSE, ACHETE-EN!",
- "LA D\220CLARATION D'IMP\342TS, NE LE VOIS-TU PAS?",
- "EH BIEN, MOI NON PLUS, CAR \267 CAUSE DE SI PETITS NUM\220ROS ET MA DIFFICULT\220 POUR BIEN VOIR DE LOIN...",
- "ON N'EN PARLE PLUS! ILS ME FONT PARA\327TRE LAID",
- "BAH! C'EST UNE BELLE F\322TE QUE LE MA\327TRE ORGANISE AVEC SES COLL\324GUES \267 CHAQUE FOIS QU'UN IMB\220CILE ARRIVE ET VEUT FINIR AVEC LUI",
+ "SI TU AS BESOIN DE QUOI QUE CE SOIT, ACH\324TE LE!",
+ "C'EST LA D\220CLARATION D'IMP\342TS, NE LE VOIS-TU PAS?",
+ "EH BIEN, MOI NON PLUS, LES CHIFFRES SONT TR\324S PETITS ET JE NE VOIS PAS BIEN DE LOIN...",
+ "PAS QUESTION. J'AI L'AIR D'UN THON AVEC",
+ "BAH! C'EST JUSTE UNE BELLE F\322TE QUE LE MA\327TRE ORGANISE AVEC DES AMIS \267 CHAQUE FOIS QU'UN IMB\220CILE VIENS POUR LE TUER",
// 20
- "D'ABORD ON LUI ARRACHE LES YEUX; APR\324S, ON LUI VERSE DE JUS DE CITRON POUR QUE \200A LUI CUISE; APR\324S...",
+ "D'ABORD ON LUI ARRACHE LES YEUX; PUIS ON VERSE DU JUS DE CITRON DANS L'ORBITE POUR QUE \200A PIQUE FORT; PUIS...",
"NON",
- "POURQUOI PAS? TU AS VU L'HEURE QU'IL EST?",
- "EN HIVER, OUI",
- "AU REVOIR",
+ "QUE VEUX-TU DIRE PAR POURQUOI PAS? TU AS VU L'HEURE QU'IL EST?",
+ "OUI, C'EST L'HIVER",
+ "\205 PLUS TARD",
// 25
"N'Y PENSES M\322ME PAS!",
- "BON, \200A VA POUR AUJOURD'HUI. JE VAIS D\327NER",
- "J'OUBLIE TOUJOURS FERMER \267 CL\220, H\220LAS!",
+ "BON, \200A SUFFIT POUR AUJOURD'HUI. JE VAIS D\327NER",
+ "J'OUBLIE TOUJOURS FERMER LA PORTE \267 CL\220!",
"QUEL ENNUI!",
- "H\220? -VOUS M'AVEZ FAIT PEUR, MA\327TRE. JE PENSAIS QUE VOUS DORMIEZ",
+ "QUOI? OH VOUS M'AVEZ FAIT PEUR, MA\327TRE. JE PENSAIS QUE VOUS DORMIEZ",
// 30
- "H\220, MA\327TRE! PRENEZ LES CL\220S DE LA SALLE DE S\220JOUR, AINSI DONC SI VOUS VOULEZ VOIR LES DESSINS ANIM\220S DEMAIN DE BONNE HEURE NE ME D\220RANGEREZ PAS",
- "QUOI? VOUS VOUS \322TES ENRHUM\220 ENCORE UNE FOIS, MA\327TRE? QUELLE CONTRARI\220T\220! JE VOUS AI D\220J\267 DIT D'Y METTRE LE CHAUFFAGE... ",
- "BON, AVALEZ UN COMPRIM\220 D'ACIDEAC\220TYL SALICYLIQUE ET ALLEZ TRANSPIRER! BONNE NUIT!",
+ "H\220, MA\327TRE! PRENEZ LES CL\220S DE LA SALLE DE S\220JOUR, AINSI VOUS POURREZ REGARDER LES DESSINS ANIM\220S DE BONNE HEURE SANS ME R\220VEILLER",
+ "VOUS VOUS \322TES ENCORE ENRHUM\220 MA\327TRE? JE VOUS AVAIS BIEN DIT DE METTRE LE CHAUFFAGE... ",
+ "BIEN, PRENEZ UNE ASPIRINE ET ALLEZ TRANSPIRER DANS VOTRE LIT! BONNE NUIT!",
},
{
// 0
@@ -5217,40 +5217,40 @@ const char *_textl[NUM_LANGS][NUM_TEXTL] = {
{
// 0
"",
- "c'est parce qu'on APPARTIENT \267 DES RACES DIFF\220RENTES ET QUE LA SOCI\220T\220 NOUS CONFRONTE, QUE NOUS ALLONS AGIR PAR NOS PLUS M\220PRISABLES INSTINCTS?",
- "NE SOMMES-NOUS PAR HASARD ACCROCHS PAR LA RAISON, L'ARME LA PLUS PUISSANTE, ET AUSSI LE DON LE PLUS PRCIEUX QUE NOUS AVONS?",
- "AH!, SI LA RAISON GUIDAIT NOS PAS DANS LA VIE SANS Y ENTRA\327NER LES SENTIMENTS, QUI FONT JAILLIR NOS INCLINAISONS PR-VOLUTIVES!",
- "NE CROIS-TU PAS QUE NOUS SERIONS PLUS HEREUX SANS CES ATTACHEMENTS-L\267? RPONDS-MOI, CRATURE PHM\324RE ",
+ "VAS-TU TE LAISSER GUIDER PAR DES INSTINCTS PRIMITIFS UNIQUEMENT PARCE QUE NOUS APPARTENONS \267 DES RACES DIFF\220RENTES ET QUE LA SITUATION SOCIALE NOUS LE DICTE?",
+ "NE SOMMES-NOUS PAR LI\220 PAR LA RAISON? L'ARME LA PLUS PUISSANTE, ET AUSSI LE DON LE PLUS PR\220CIEUX QUE NOUS AVONS?",
+ "AH! SI NOUS LAISSIONS LA RAISON NOUS GUIDER DANS LA VIE SANS LAISSER DE PLACE AUX SENTIMENTS, QUI FONT REJAILLIR NOS INCLINAISONS PR\220-\220VOLUTIVES!",
+ "R\220PONDS-MOI, CR\220ATURE \220PH\220M\324RE. NE CROIS-TU PAS QUE NOUS SERIONS TOUS PLUS HEUREUX SANS CES ATTACHEMENTS \202MOTIONELS? ",
// 5
"TU NE PASSES PAS",
- "TU VOIS? C'EST UN EXEMPLE CLAIR: TOI, TU VEUX PASSER ET POURSUIVRE TON AVENTURE ET MOI, JE NE PEUX PAS LE TOLRER",
- "CELA DOIT \322TRE UNE RAISON SUFFISANTE DE CONFLIT ENTRE NOUS DEUX, QUI NE NOUS CONNAISSONS DE RIEN?",
- "C'EST CELA",
- "EH BIEN, CELA DPEND DE CE QU'ON ENTEND PAR RLATION. D'APR\324S QUELQUES AUTEURS...",
+ "TU VOIS, C'EST UN EXEMPLE CLAIR: TOI, TU VEUX PASSER ET POURSUIVRE TON AVENTURE ET MOI, JE NE PEUX PAS TE LAISSER FAIRE",
+ "CELA SERA-T-IL UNE RAISON SUFFISANTE DE CONFLIT ENTRE NOUS, QUI NOUS CONNAISSONS \267 PEINE?",
+ "ET BIEN",
+ "EN FAIT, CELA D\220PEND DE CE QU'ON ENTEND PAR RELATION. D'APR\324S CERTAINS AUTEURS...",
// 10
- "LA CHASSE COMME MOYEN DE SUBSISTANCE EST UNE ACTIVIT\220 ARCHA\330QUE, INCOMPATIBLE AVEC ma NATURE SUP\220RIEURE . ET DE PLUS JE SUIS DEVENU V\220G\220TARIEN",
- "IL S'EN SUIT QU'EN TRAIN DE D\220VORER UN TYPE, JE ME SUIS MIS \267 R\220FL\220CHIR. ALORS, SUIS ARRIV\220 \267 LA CONCLUSION MENTIONN\220 CI-DESSUS",
- "ABANDONNER CES HABITUDES M'EST REVENU CHER, MAIS \267 LA FIN MON \266ME IRASCIBLE a vancue MON \266ME CONCUPISCIBLE, ET D\324S LORS JE N'AI GO\352T\220 \267 LA VIANDE",
- "M\322ME PAS LE PLAISIR DE CROQUER UN OS, AVEC LE SUC DE LA PEAU ENTRE SES PORES ET sa SAVEUR QUI TE TRANSPORTE VERS DES LIEUX TR\324S LONTAINS, PARADISIAQUES...",
- "CECI NE M'AFFECTE PAS M\322ME, ABSOLUMENT PAS, C'EST VRAI",
+ "EURK! LA CHASSE COMME MOYEN DE SUBSISTANCE EST UNE ACTIVIT\220 ARCHAIQUE, INCOMPATIBLE AVEC MA NATURE SUP\220RIEURE. ET DE PLUS JE SUIS DEVENU V\220G\220TARIEN",
+ "EN FAIT J'\220TAIS JUSTEMENT EN TRAIN DE D\220VORER UN TYPE QUAND J'AI COMMENC\220 \267 R\220FL\220CHIRET QUE JE SUIS ARRIV\220 \267 LA CONCLUSION MENTIONN\220E CI-DESSUS",
+ "CELA M'A PRIS DU TEMPS POUR ABANDONNER CES VIELLES HABITUDES, MAIS \267 LA FIN MON \266ME IRASCIBLE A VAINCUE MON \266ME CONCUPISCIBLE, ET D\324S LORS JE N'AI PLUS GO\352T\220 \267 LA VIANDE",
+ "M\322ME PAS LE PLAISIR DE CROQUER UN OS, AVEC LE GOUT DE LA PEAU ET LA MERVEILLEUSE SAVEUR DE LA MOELLE ...QUI TE TRANSPORTE VERS DES LIEUX TR\324S LONTAINS, PARADISIAQUES...",
+ "M\322ME CECI NE M'AFFECTE PAS, ABSOLUMENT PAS",
// 15
"QUOI?",
- "JE NE SAIS PAS DE QUOI TU M'EN PARLES, CR\220ATURE \220PH\220M\324RE",
- "CELA NE ME REGARDE PAS",
- "LES AUTRES JEUX, JE NE SAIS PAS; MAIS CELUI-CI OUI, POUR EN PROFITER DE CET \220CRAN SI JOLI",
+ "JE NE SAIS PAS DE QUOI TU PARLES, CR\220ATURE \220PH\220M\324RE",
+ "CELA NE M'INTERRESSE PAS",
+ "JE NE SAIS PAS POUR LES AUTRES JEUX; MAIS ON POURRAIT UTILISER CET \220CRAN SI JOLI",
"",
// 20
- "MOI, JE NE M'EN FICHERAI PAS",
- "NON, C'EST QU'ILS SONT LE GRAND-P\324RE, LE P\324RE, LE FILS, ET UN AMI QUI S'APPELLE COMME \200A",
- "NON, MAIS SI NON, IL VA PARA\327TRE QU'ON A FAIT LE JEU ENTRE CINQ",
+ "MOI, JE NE M'EN FICHERAI PAS...",
+ "NON, C'EST JUSTE LE GRAND-P\324RE, LE P\324RE, LE FILS, ET UN AMI QUI S'APPELLE COMME \200A",
+ "MAIS ON VA AVOIR l'IMPRESSION QUE LE JEUX A \220T\220 FAIT PAR CINQ PERSONNES",
"CES GAR\200ONS ONT DU FUTUR",
- "CELUI-L\267 EST BON! CELUI-L\267 EST BON!",
+ "ELLE EST BONNE! VRAIMENT BONNE!",
// 25
"APPELLE-MOI CONSTANTIN",
- "CE N'\220TAIT PAS MOI, MON VIEUX. C'\220TAIT MON DOUBLE, LE COYOTE",
- "TIENS! QUELS CR\220DITS SI LONGS",
- "J'AI D\220J\267 PERDU LES COMPTES",
- "EH BIEN, D\220SID\324RE, QUE T'ARRIVERA-T-IL MAINTENANT?",
+ "CE N'\220TAIT PAS MOI, MEC. C'\220TAIT MON DOUBLE, \"LE COYOTE\"",
+ "PUT..., ILS SONT VRAIMENT LONG CES CR\220DITS!",
+ "CELA FAIT LONGTEMPS QUE J'AI PERDU LE COMPTE",
+ "EH BIEN, D\220SIR\220, QUE VA T'IL T'ARRIVER MAINTENANT?",
// 30
"MAIS TU DEVRAIS MAIGRIR",
"JE VAIS ME RETIRER AU TIBEL POUR Y R\220FL\220CHIR SUR LE SENS DE LA VIE",
@@ -5380,28 +5380,28 @@ const char *_textp[NUM_LANGS][NUM_TEXTP] = {
{
// 0
"",
- "BONJOUR!",
- "JOLIE, OUI M'SIEUR",
- "NON, QU'ELLE NE LE FAIT PAS",
- "D'ACCORD, A VA",
+ "SALUT!",
+ "OUI M'SIEUR, TR\212S JOLIE",
+ "NON, JE NE PEUX PAS",
+ "BON D'ACCORD",
// 5
- "OUI?",
- "ET QUOI?",
- "SUIS DSOL. LE SYNDICAT DE PIANISTES NE ME PERMET PAS DE LIBRER LES FILLES DES GRIFFES DE VAMPIRES",
- "SI ELLE AVAIT T RAPTE PAR UN HOMME-LOUP...",
- "JE NE PEUX PAS JOUER DAVANTAGE CETTE CHANSON",
+ "VRAIMENT?",
+ "ET?",
+ "JE SUIS D\220SOL\220. LE SYNDICAT DES PIANISTES NE ME PERMET PAS DE LIB\220RER LES FILLES DES GRIFFES DE VAMPIRES",
+ "SI ELLE AVAIT \220T\220 KIDNAPP\220 PAR UN LOUP-GAROU...",
+ "JE NE SAIS JOUER QUE CETTE CHANSON",
// 10
- "JE SUIS LE PIANISTE DU CONSERVATOIRE ET LE TAVERNIER N'ACH\324TE PAS MES PARTITIONS",
- "ET MOI QUI AIME BEAUCOUP LA MUSIQUE CLASSIQUE!",
- "PARCE QUE J'AI DES TAMPONS \267 L'OU\330E",
- "PARCE QUE JE LIS LES LEVRES",
+ "JE SUIS PIANISTE AU CONSERVATOIRE ET LE TAVERNIER NE VEUT PAS M'ACHETER D'AUTRES PARTITIONS",
+ "ET POURTANT, J'AIME VRAIMENT LA MUSIQUE CLASSIQUE!",
+ "C'EST PARCE QUE J'AI DES BOULES QUI\212S",
+ "C'EST PARCE QUE JE SAIS LIRE SUR LES L\212VRES",
"NOOON!",
// 15
- "QUE NON! QUE JE NE PEUX PAS ME RETENIR DAVANTAGE!",
- "QUE NOOOOOON!",
+ "NON! JE NE SUPPORTE PLUS CETTE MUSIQUE!",
+ "PAS QUESTIOOONNN!",
"QUOI? BIEN S\352R QUE \200A M'INT\220RESSE",
- "MAINTENANT JE POURRAI JOUER UNE AUTRE CHANSON -QUEL SOULAGEMENT!",
- "TU PEUX GARDER MES TAMPONS, JE SUPPOSE",
+ "DIEUX MERCI. JE VAIS POUVOIR JOUER UN AUTRE MORCEAU MAINTENANT!",
+ "TU PEUX GARDER MES BOULES QUI\212S, JE SUPPOSE",
},
{
// 0
@@ -5532,34 +5532,34 @@ const char *_textt[NUM_LANGS][NUM_TEXTT] = {
{
// 0
"",
- "QU'EST-CE QU'IL Y A, QU'Y A-T-IL?",
- "D'ACCORD. CHAMBRE 512. PAR LES ESCALIERS. LA CL EST SUR LA PORTE",
+ "QU'EST-CE QU'IL Y A? QUEL EST LE PROBL\212ME?",
+ "D'ACCORD. CHAMBRE 512. EN HAUT. LA CL\220 EST SUR LA PORTE",
"LE COMTE DRASCULA?!",
- "NON, RIEN, CE TYPE A UNE MAUVAISE R\220PUTATION PAR ICI",
+ "NON, RIEN, CE TYPE A JUSTE UNE MAUVAISE R\220PUTATION PAR ICI",
// 5
- "EH BIEN, IL Y A DES RUMEURS QUI COURENT SUR LUI. CERTAINS DISENT QUE C'EST UN VAMPIRE ET QU'IL ENLEVE DU MONDE POUR SUCER LEUR SANG ",
- "MAIS D'AUTRES PENSENT QU'IL EST SEULEMENT UN TRAFICANT D'ORGANES, ET C'EST POUR CELA QUE DES PERSONNES DPECES SONT APPARUES DANS LES ALENTOURS",
- "CERTAINEMENT IL NE S'AGIT QUE DES BRUITS QUI COURENT. S\352REMENT IL AURA LES DEUX MTIERS. MAIS, POURQUOI VOULEZ-VOUS VOIR CE TYPE?",
- "OH, NON! OUBLIEZ CELA, J'AI BEAUCOUP \267 FAIRE.",
- "BON, A VA. MAIS PARCE QUE JE VEUX ET NON PARCE QUE TU LE DIS",
+ "EH BIEN, IL Y A DES RUMEURS QUI COURENT SUR LUI. CERTAINS DISENT QUE C'EST UN VAMPIRE ET QU'IL ENL\212VE DES GENS POUR SUCER LEUR SANG ",
+ "MAIS D'AUTRES PENSENT QU'IL EST SEULEMENT UN TRAFICANT D'ORGANES, ET QUE C'EST POUR \200A QUE DES MORCEAUX DE CORPS TRA\214NE PARTOUT",
+ "MAIS BIEN S\352R, CE NE SONT QUE DES RUMEURS. IL EST S\352REMENT LES DEUX. MAIS, POURQUOI VOULEZ-VOUS LE RENCONTRER?",
+ "OH, NON! OUBLIEZ \200A, JE SUIS TR\212S OCCUP\220...",
+ "BON, D'ACCORD. MAIS C'EST PARCE QUE JE LE VEUX, PAS PARCE QUE TU LE DEMANDES",
// 10
"ILS GAGNENT",
"FICHE-MOI LA PAIX, D'ACCORD?",
- "C'EST S\352R, JE NE SUIS PAS UN AVUGLE",
- "SELON LA TRADITION DU VILLAGE, QUAND IL Y A UN MATCH ON OUBLIE LES RANCUNES, POUR ALLER ENCOURAGER LA S\220LECTION",
- "ET FERME-LA D'UNE FOIS!, JE NE PEUX PAS ENTENDRE",
+ "BIEN S\352R. JE NE SUIS PAS AVEUGLE",
+ "SELON LA TRADITION DU VILLAGE, QUAND IL Y A UN MATCH ON OUBLIE LES RANCUNES, POUR ENCOURAGER L'\220QUIPE LOCALE",
+ "ET FERME-LA!, JE N'ENTENDS RIEN!",
// 15
- "ALLEZ, VA-T'-EN ET NE D\220RANGES PAS!",
- "\200A VIENT DE COMMENCER! -ET TAIS-TOI!",
- "AH, BON! JE PENSAIS QU'IL SE PASSAIT QUELQUE CHOSE",
- "NON, C'EST \220GAL. \267 CES HEURES-CI ELLE SERA D\220J\267 MORTE",
- "LE FAIT EST QU'ELLE A COMMENC\220 JOUER DE LA MUSIQUE CLASSIQUE ET QUE JE NE SUPPORTE PAS CELA",
+ "ALLEZ, VA-T'EN ET NE ME D\220RANGES PLUS",
+ "\200A VIENT DE COMMENCER! TA GUEULE!",
+ "OK, OK! JE PENSAIS QU'IL SE PASSAIT QUELQUE CHOSE",
+ "\200A N'A PLUS D'IMPORTANCE. ELLE EST CERTAINEMENT MORTE \267 L'HEURE QU'IL EST",
+ "IL A COMMENC\220 \267 JOUER DE LA MUSIQUE CLASSIQUE ET JE NE L'AI PAS SUPPORT\220",
// 20
- "ET MAINTENANT QUE JE LUI AI MIS DEHORS, COMMENT JE LUI PAIE POUR QU'IL JOUE CE QUE JE VEUX",
- "ET ENCORE IL S'EST MONTR\220 ARROGANT... -ET DIRE QU'IL PARAISSAIT UNE SAINTE-NITOUCHE!",
- "...FAITES ATTENTION, ON VIENT DE CIRER LE PARQUET",
- "SILENCE! ON VOIT LE MATCH!",
- "OH L\267 L\267! TIENS!",
+ "COMME JE LE PAIE POUR QU'IL JOUE CE QUE JE VEUX, JE L'AI VIR\220",
+ "ET ALORS IL S'EST MONTR\220 ARROGANT... ET DIRE QU'IL PARAISSAIT SI GENTIL ET INNOCENT... QUEL HYPOCRITE!",
+ "EN FAIT, FAITES ATTENTION, JE VIENS DE CIRER LE PARQUET",
+ "SILENCE! ON REGARDE LE MATCH!",
+ "OH, ALLEZ! PRENDS LA!",
},
{
// 0
@@ -5834,80 +5834,80 @@ const char *_textvb[NUM_LANGS][NUM_TEXTVB] = {
{
// 0
"",
- "QUI DIABLE APPELLE \267 CETTE HEURE-CI?",
- "H... NON, NON. JE SUIS LE NAIN GANYM\324DE... LE PROFESSEUR VON BRAUN... N'HABITE DJ\267 L\267",
- "NON, JE NE SAIS PAS O\353 IL EST!",
- "VA-T'-EN!",
+ "QUI DIABLE SONNE \267 CETTE HEURE-CI?",
+ "OH, ..OH, NON, NON. JE SUIS... LE NAIN GANYM\324DE... LE PROFESSEUR VON BRAUN... IL N'HABITE PLUS L'\267",
+ "NON, JE NE SAIS PAS O\353 IL HABITE MAINTENANT!",
+ "VA-T'EN!",
// 5
- "IMBCIL! C'EST DJ\267 TROP TARD! C'EST TOUJOURS TROP TARD!",
- "JE SUIS TOUT-\267-FAIT CONFORME",
- "MOI PEUR?",
- "RENSEIGNE-TOI, MON GARS: TU PARLES AVEC LE SEUL QUI CONNA\327T LE SECRET POUR CONFRONTER LES VAMPIRES",
- "TOUT LE MONDE N'EST PAS CAPABLE DE LUTTER AVEC UN VAMPIRE. IL FAUT DES QUALITS SPCIALES",
+ "IMB\220CILE! C'EST D\220J\267 TROP TARD! C'EST TOUJOURS TROP TARD!",
+ "JE SUIS TOUT-\267-FAIT D'ACCORD",
+ "MOI? PEUR?",
+ "RENSEIGNE-TOI, MON GARS: TU PARLES AVEC LA SEULE PERSONNE QUI CONNA\327T LE SECRET POUR AFFRONTER LES VAMPIRES",
+ "TOUT LE MONDE N'EST PAS CAPABLE DE LUTTER CONTRE UN VAMPIRE. IL FAUT DES QUALIT\220S SP\220CIALES",
// 10
"TU NE LES A PAS",
- "EST-CE QUE TU PARIE TOUT L'ARGENT QUE TU AS \267 QUE C'EST NON?",
- "\200A VA, ENTRE",
- "SI EN V\220RIT\220 TU TE SENS CAPABLE DE CONFRONTER DRASCULA, IL FAUDRA QUE TU SUPPORTES TOUS LES BRUITS GRIN\200ANTS ET SU\200ANTS",
+ "TU VEUX PARIER TOUT TON ARGENT QUE TU NE LES AS PAS?",
+ "D'ACCORD, ENTRE",
+ "SI TU VEUX POUVOIR AFFRONTER DRASCULA, IL FAUT QUE TU PUISSES SUPPORTER LES BRUITS GRIN\200ANTS ET AUTRES BRUITS DE VAMPIRES",
"C'EST CLAIR?",
// 15
"D'ACCORD. ATTENDS UN INSTANT",
- "METS-TOI AU MILIEU DE LA CHAMBRE, S'IL TE PLA\327T",
- "VOYONS! O\353 EST-CE QUE J'AI MIS LE DISQUE \"ONGLES GRATTANT UN TABLEAU\"?",
+ "PLACES-TOI AU MILIEU DE LA PI\212CE, S'IL TE PLA\327T",
+ "VOYONS! O\353 AIS-JE MIS LE DISQUE \"ONGLES GRATTANT UN TABLEAU NOIR\"?",
"TR\324S BIEN. ON Y VA",
"TU VOIS? TU ES UN INCAPABLE, COMME TOUS LES AUTRES",
// 20
- "MAINTENANT DONNE-MOI L'ARGENT QUE TU AS PERDU ET VA-T'-EN",
- "ET N'Y REVIENS \267 MOINS QUE TU SOIS COMPL\324TEMENT EN FORME",
- "ET QUE VEUX-TU MAINTENANT?",
+ "MAINTENANT DONNE-MOI L'ARGENT. TU AS PERDU ALORS VA-T'EN",
+ "ET NE REVIENS PAS AVANT D'\322TRE COMPL\324TEMENT PR\322T",
+ "QU'EST-CE QUE TU VEUX MAINTENANT?",
"JE DOIS LE RECONNA\327TRE... TU AS DES APTITUDES POUR LUTTER CONTRE LES VAMPIRES",
- "EN FAIT, TIENS TON ARGENT. JE SAIS RECONNA\327TRE MES ERREURS",
+ "EN FAIT, REPREND TON ARGENT. JE SAIS RECONNA\327TRE MES ERREURS",
// 25
- "MAINTENANT VA-T'-EN, JE VEUX DORMIR UN PEU ",
- "QUAND TU SOIS PR\322T \267 TE CONFRONTER AVEC UN VAMPIRE, REVIENS ET TU POURRAS COMPTER SUR MOI",
- "OH! C'EST FACIL. AVEC LA LUMI\324RE DU SOLEIL OU UN CRUCIFIX TU LE R\220DUIT EN CENDRES",
- "MAIS TU DOIS FAIRE SP\220CIALE ATTENTION AVEC DRASCULA. GR\266CE \267 SES POUVOIRS FRISYSHNOSTIQUES C'EST LE PLUS PUISSANT DES VAMPIRES",
- "JE SERAIS PERDU SI CE N'EST PAS POUR LA...",
+ "MAINTENANT VA-T'EN, JE VEUX DORMIR UN PEU ",
+ "QUAND TU ES PR\322T \267 TE CONFRONTER AVEC UN VAMPIRE, REVIENS ET JE T'AIDERAI",
+ "OH! C'EST FACILE. PREND CE CRUCIFIX. JUSTE AVEC SA LUMI\324RE TU R\220DUIT UN VAMPIRE EN CENDRES",
+ "MAIS TU DOIS FAIRE SP\220CIALEMENT ATTENTION AVEC DRASCULA. GR\266CE \267 SES POUVOIRS FRISYSHNOSTIQUES C'EST LE PLUS PUISSANT DES VAMPIRES",
+ "TU SERAIS PERDU SANS CETTE...",
// 30
"...POTION!",
- "OH, BIEN S\352R. TU AS RAISON, SI JE CONTINUE \267 DORMIR COMME \200\265, J'AURAI DES PROBL\324MES DE COLONNE DANS MA VIEILLESSE",
- "bon, J'ACCEPTE QU'IL A \220T\220 MEILLEURE ADVERSAIRE QUE MOI, MAIS JE ME SUIS GARD\220 LE DOS GR\266CE \267 MA BONNE TROUVAILLE DANS L'\220TUDE DE TECHNIQUES ANTI-VAMPIRES",
- "J'AI D\220COUVERT UNE POTION IMMUNOLOGIQUE QUI TE FAIT INVULN\220RABLE CONTRE N'IMPORTE QUELLE MORSURE DE VAMPIRE OU SES POUVOIRS FRSYSSHNOTIQUES",
- "NON, EXCUSES-MOI. JE L'AI EUE, MAIS UNE POTION DE CES CARACT\220RISTIQUES EST DANGEREUSE. IMAGINE TOI SI ELLE TOMBAIT DANS LES MAINS D'UN VAMPIRE",
+ "OUI, TU AS RAISON. JE RISQUE D'AVOIR DES PROBL\324MES DE DOS PLUS TARD SI JE CONTINUE \267 DORMIR COMME \200A",
+ "D'ACCORD, IL \220T\220 MEILLEUR QUE MOI, MAIS TU DOIS ADMETTRE QUE MES D\220COUVERTES SUR LES TECHNIQUES ANTI-VAMPIRES M'ONT SAUVER LA VIE",
+ "J'AI D\220COUVERT UNE POTION QUI TE REND INVULN\220RABLE CONTRE LES MORSURES D'UN VAMPIRE OU SES POUVOIRS FRSYSSHNOTIQUES",
+ "NON, NON, EXCUSES-MOI. JE L'AI EUE, MAIS C'EST TR\324S DANGEREUX DE GARDER UNE TELLE POTION. IMAGINE CE QUI ARRIVERAIT SI ELLE TOMBAIT DANS LES MAINS D'UN VAMPIRE",
// 35
- "IL DEVIENDRAIT IMMUNIS\220 AUX AILS, \267 LA LUMI\324RE DU SOLEIL. DONC, J'AI D\352 M'EN D\220BARRASSER DES EXC\220DENTS PAR LA SCIENTIFIQUE M\220THODE DE LES JETER AUX \220GOUTS",
- "RESTE TRANQUILLE, JE ME SOUVIENS PARFAITEMENT DE LA PR\220PARATION DE CETTE POTION",
- "il me faut D'AIL, MAIS J'EN AI D\220J\267. IL FAUDRA UN PEU DE CIRE, DE LA NICOTINE, UN CHEWING-GUM ET UN PAPIER \267 CIGARETTES, OU QUELQUE CHOSE PAREILLE",
- "AH! ET CERTAINEMENT, L'INGR\220DIANT PRINCIPAL: LES FEUILLES D'UNE \220TRANGE PLANTE APPEL\220E FERNAN",
- "IL S'AGIT D'UNE PLANTE GRIMPANTE DONT LES FEUILLES FOURNISSENT DES POUVOIRS MAGIQUES SI ON LES COUPE AVEC UNE FAUCILLE EN OR",
+ "IL SERAIT IMMUNIS\220 CONTRE L'AIL ET LA LUMI\324RE DU SOLEIL. DONC, J'AI D\352 M'EN D\220BARRASSER PAR LA M\220THODE SCIENTIFIQUE DE LA JETER DANS LES TOILETTES",
+ "NE T'INQUI\324TES PAS, JE ME SOUVIENS PARFAITEMENT DE LA PR\220PARATION DE CETTE POTION",
+ "IL ME FAUT DE L'AIL, MAIS J'EN AI D\220J\267. IL FAUDRA UN PEU DE CIRE, DE LA NICOTINE, UN CHEWING-GUM ET DU PAPIER \267 CIGARETTES, OU QUELQUE CHOSE DE SEMBLABLE",
+ "OH... ET L'INGR\220DIANT PRINCIPAL: LES FEUILLES D'UNE \220TRANGE PLANTE APPEL\220E FERNAN",
+ "C'EST UNE PLANTE GRIMPANTE DONT LES FEUILLES ONT DES POUVOIRS MAGIQUES SI ON LES COUPE AVEC UNE FAUCILLE EN OR",
// 40
- "TU LE SAIS D\220J\267. D\324S QUE TU AURAS CES CINQ TRUCS APPORTE-LES-MOI ET JE TE PR\220PARERAI LA POTION",
- "APR\324S TU SERAS PR\322T POUR LUTTER CONTRE DRASCULA",
- "RAPPELLE-TOI: DE LA CIRE, DE LA NICOTINE, UN CHEWING-GUM, UN PAPIER ET LES FEUILLES DE FERNAN, LA PLANTE, COUP\220ES AVEC UNE FAUCILLE EN OR",
- "JE TE L'AI D\220J\267 DIT! TOUT A \220T\220 GR\266CE \267 LA POTION",
- "AH! TR\324S BIEN! ALORS, JE VAIS ME PR\220PARER LE PO... LA POTION. JE N'AI QUE POUR UN MOMENT",
+ "DONC D\324S QUE TU AS CES CINQ INGR\220DIENTS, APPORTE-LES-MOI ET JE TE PR\220PARERAI LA POTION",
+ "ENSUITE TU SERAS PR\322T POUR LUTTER CONTRE DRASCULA",
+ "SOUVIENS-TOI: DE LA CIRE, DE LA NICOTINE, UN CHEWING-GUM, UN PAPIER ET DES FEUILLES DE FERNAN COUP\220ES AVEC UNE FAUCILLE EN OR",
+ "JE TE L'AI D\220J\267 DIT! TOUT \200A GR\266CE \267 LA POTION",
+ "AH! TR\324S BIEN! JE VAIS PR\220PARER LE P\220T... LA POTION. JE N'EN AI QUE POUR UN MOMENT",
// 45
- "C'EST UN SORTIL\324GE DE PROTECTION CONTRE-VAMPIRES",
- "JE L'AI MIS POUR DISSIMULER, LE DESSINATEUR AYANT OUBLI\220 D'Y METTRE LA FEN\322TRE QU'ON VOIT DU DEHORS",
- "EH BIEN, CE QUE TU DOIS SAVOIR D'ABORD C'EST LA FA\200ON D'ALLER AU CH\266TEAU DE DRASCULA",
- "IL Y A UNE GROTTE QUI M\324NE DIRECTEMENT AU CH\266TEAU ET QU'IGOR, CE FOU FAN D'ELVIS, EN UTILISE POUR SE RENDRE AU VILLAGE LE MATIN",
- "MAIS FAIT ATTENTION, ELLE TOUJOURS GARD\220E PAR UN VAMPIRE. TU DEVRAS T'EN D\220BARRASSER",
+ "C'EST UN SORTIL\324GE DE PROTECTION CONTRE LES VAMPIRES",
+ "JE L'AI MIS L\267 POUR PR\220TENDRE QUE LE DESSINATEUR N'A PAS OUBLI\220 D'Y METTRE LA FEN\322TRE QU'ON VOIT DU DEHORS",
+ "EH BIEN, LA PREMI\324RE CHOSE QUE TU DOIS SAVOIR C'EST LE CHEMIN POUR ALLER AU CH\266TEAU DE DRASCULA",
+ "IL Y A UNE GROTTE QUI M\324NE DIRECTEMENT AU CH\266TEAU. IGOR, LE SERVANT FOU, L'UTILISE POUR SE RENDRE AU VILLAGE CHAQUE MATIN",
+ "MAIS FAIT ATTENTION, IL Y A TOUJOURS UN VAMPIRE QUI GARDE CE CHEMIN. TU DEVRAS T'EN D\220BARRASSER",
// 50
"IL Y A UN VIEUX PUITS \267 C\342T\220 DE LA CHAPELLE DU CIMETI\324RE",
- "ON L'UTILISAIT JADIS POUR JUGER DES AFFAIRES DE SORCELLERIE",
- "ON JETAIT LES SORCI\324RES AU PUITS. SI ELLES COULAIENT, ON \220TAIT S\352R; AUTREMENT, PAS",
- "UNE FOIS ON A JET\220 UNE ET ELLE N'AS PAS COUL\220, DONC ELLE NE SERAIT PAS UNE SORCI\324RE",
- "MAINTENANT GARDE \267 LA QUESTION QUI NOUS INT\220RESSE: TIENS TA POTION, MAIS ON NE M'A PAS DONN\220 QUE POUR UN SEULEMENT.",
+ "ON L'UTILISAIT JADIS POUR JUGER LES AFFAIRES DE SORCELLERIE",
+ "ON JETAIT LES SORCI\324RES DANS LE PUITS. SI ELLES COULAIENT C'\220TAIENT DES VRAIS SORCI\324RES, SINON C'EN \220TAIENT PAS",
+ "UNE FOIS ON EN A JET\220 UNE ET ELLE N'A PAS COUL\220. JE SUPPOSE QUE CE N'\220TAIT PAS UNE SORCI\324RE",
+ "ENFIN BON, VOICI TA POTION. MAIS JE N'EN AI FAIS ASSEZ QUE POUR UN USAGE",
// 55
- "IL VAUDRA MIEUX QUE TU LA FUMES JUSTE AVANT DE CONFRONTER DRASCULA",
- "COURS!",
- "DES EXCUSES!",
- "JHON HACKER? SUIS LE DOCTEUR VON BRAUN",
- "\220COUTEZ-MOI. C'EST TR\324S IMPORTANT. C'EST SUR LA POTION",
+ "IL VAUDRAIT MIEUX QUE TU LA FUMES JUSTE AVANT DE CONFRONTER DRASCULA",
+ "ALLEZ, VAS-Y!",
+ "OH, EXCUSEZ...!",
+ "JOHN HACKER? C'EST LE DOCTEUR VON BRAUN",
+ "\220COUTEZ-MOI, C'EST TR\324S IMPORTANT. C'EST \267 PROPOS DE LA POTION",
// 60
- "DANS UN LIVRE QUE J'AI TROUV\220 SUR LES POTIONS, IL DIT QUE VOUS NE DEVEZ PAS BOIR DE L'ALCOOL APR\324S AVOIR FUM\220 LA POTION ",
- "L'ALCOOL AVAL\220 R\220AGIT AVEC LES INGR\220DIENTS DE LA POTION ET ANNULE SES EFFETS EN DIXI\324MES DE SECONDE",
- "JE DOIS RACCROCHER. SUIS CHERCH\220 PAR LA POLICE. ILS DISENT QUE JE TRAFIQUE EN DROGUES -IGNORANTS! AU REVOIR ET BONNE CHANCE EN SAUVANT LE MONDE",
+ "TA GUEULE ET LAISSE MOI PARLER. JE VIENS DE TROUVER CE LIVRE SUR LES POTIONS ANTI-VAMPIRES QUI MET EN GARDE CONTRE LE M\220LANGE DE LA CIGARETTE AVEC DE L'ALCOOL",
+ "L'ALCOOL R\220AGIT AVEC LES INGR\220DIENTS DE LA POTION ET ANNULE SES EFFETS EN QUELQUES SECONDES",
+ "JE DOIS RACCROCHER. LA POLICE ME RECHERCHE. ILS DISENT QUE JE TRAFIQUE EN DROGUES. LES IDIOTS! AU REVOIR ET BONNE CHANCE POUR SAUVER LE MONDE!",
},
{
// 0
@@ -6010,10 +6010,10 @@ const char *_textsys[NUM_LANGS][NUM_TEXTSYS] = {
"STIMME UND TEXT",
},
{
- "APPUYEZ \267 NOUVEAU SUR SUPR POUR COMMENCER",
- "APPUYEZ \267 NOUVEAU SUR ESC POUR SORTIR",
- "SEULEMENT DES VOIX",
- "VOIX ET TEXT",
+ "APPUYEZ \267 NOUVEAU SUR 'SUPR' POUR RECOMMENCER",
+ "APPUYEZ \267 NOUVEAU SUR 'ESC' POUR SORTIR",
+ "VOIX UNIQUEMENT",
+ "VOIX ET TEXTE",
},
{
"PREMI DI NUOVO CANC PER RICOMINCIARE",
@@ -6041,17 +6041,17 @@ const char *_texthis[NUM_LANGS][NUM_TEXTHIS] = {
},
{
"",
- "",
- "",
- "",
- ""
+ "Vor einer langen Zeit scheint Drascula Von Brauns Frau getoetet zu haben und als Von Braun sich dann den Grafen vorknoepfen wollte, begann er damit, alles, was er ueber Vampire finden konnte, in Erfahrung zu bringen.",
+ "Als er glaubte, er sei bereit, ging er hoch zum Schloss und hatte eine aeusserst gewaltsame Auseinandersetzung mit Drascula.",
+ "Niemand weiss, was genau dort passierte. Obwohl Von Braun verlor, konnte Drascula ihn nicht toeten.",
+ "Von Braun fuehlte sich durch seine Niederlage gedemuetigt. Er rannte vom Schloss fort und wagte es nie wieder, Drascula gegenueberzutreten."
},
{
"",
- "",
- "",
- "",
- ""
+ "Il y a longtemps, Drascula apparemment tua la femme de Von Braun, et ainsi, pour pouvoir confronter le comte, Von Braun commen\207a ses recherches sur les vampires.",
+ "Quand il pensa \322tre pr\322t, il se rendi au ch\266teau et eu une altercation violente avec Drascula.",
+ "Personne ne sait exactment ce qu'il s'est pass\220 ce jour l\267. Bien que Von Braun perdi, Drascula ne p\352 pas le tuer.",
+ " Von Braun se senti humili\220 par sa d\220faite. Il s'enfuit du ch\266teau et n'osa plus jamais affronter Drascula."
},
{
"",
@@ -6174,12 +6174,12 @@ const char *_textverbs[NUM_LANGS][NUM_TEXTVERBS] = {
"Druecke",
},
{
- "regardez",
- "ramassez",
- "ouvrez",
- "fermez",
- "parlez",
- "poussez",
+ "regarder",
+ "ramasser",
+ "ouvrir",
+ "fermer",
+ "parler",
+ "pousser",
},
{
"guarda",
@@ -6209,7 +6209,7 @@ const char *_textmisc[NUM_LANGS][NUM_TEXTMISC] = {
"GOOOOOOOAAAAAAAL!",
},
{
- "HUNCHBACKED",
+ "BOSSU",
"Transilvania, 1993 d.c.",
"GOOOOOOOAAAAAAAL!",
},
diff --git a/tools/create_hugo/create_hugo.cpp b/tools/create_hugo/create_hugo.cpp
index 0f460c9268..031804f8e1 100644
--- a/tools/create_hugo/create_hugo.cpp
+++ b/tools/create_hugo/create_hugo.cpp
@@ -25,6 +25,9 @@
* data file, used by the game engine
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
@@ -49,6 +52,7 @@
#include "staticparser.h"
#include "staticschedule.h"
#include "staticutil.h"
+#include "staticfont.h"
static void writeByte(FILE *fp, uint8 b) {
fwrite(&b, 1, 1, fp);
@@ -307,13 +311,48 @@ int main(int argc, char *argv[]) {
writeTextArray(outFile, textEngine, NUM_ENGINE_TEXT);
// Write textIntro
- writeTextArray(outFile, textIntro, NUM_INTRO_TEXT);
+ writeTextArray(outFile, textIntro_dummy, NUM_INTRO_TEXT_DUMMY);
+ writeTextArray(outFile, textIntro_dummy, NUM_INTRO_TEXT_DUMMY);
+ writeTextArray(outFile, textIntro_v3, NUM_INTRO_TEXT_V3);
+ writeTextArray(outFile, textIntro_dummy, NUM_INTRO_TEXT_DUMMY);
+ writeTextArray(outFile, textIntro_dummy, NUM_INTRO_TEXT_DUMMY);
+ writeTextArray(outFile, textIntro_v3, NUM_INTRO_TEXT_V3);
// Write x_intro and y_intro
- writeUint16BE(outFile, NUM_INTRO_TICK);
- for (i = 0; i < NUM_INTRO_TICK; i++) {
- writeByte(outFile, x_intro[i]);
- writeByte(outFile, y_intro[i]);
+ writeUint16BE(outFile, NUM_INTRO_TICK_DUMMY);
+ for (i = 0; i < NUM_INTRO_TICK_DUMMY; i++) {
+ writeByte(outFile, x_intro_dummy[i]);
+ writeByte(outFile, y_intro_dummy[i]);
+ }
+
+ writeUint16BE(outFile, NUM_INTRO_TICK_DUMMY);
+ for (i = 0; i < NUM_INTRO_TICK_DUMMY; i++) {
+ writeByte(outFile, x_intro_dummy[i]);
+ writeByte(outFile, y_intro_dummy[i]);
+ }
+
+ writeUint16BE(outFile, NUM_INTRO_TICK_V3);
+ for (i = 0; i < NUM_INTRO_TICK_V3; i++) {
+ writeByte(outFile, x_intro_v3[i]);
+ writeByte(outFile, y_intro_v3[i]);
+ }
+
+ writeUint16BE(outFile, NUM_INTRO_TICK_V1D);
+ for (i = 0; i < NUM_INTRO_TICK_V1D; i++) {
+ writeByte(outFile, x_intro_v1d[i]);
+ writeByte(outFile, y_intro_v1d[i]);
+ }
+
+ writeUint16BE(outFile, NUM_INTRO_TICK_DUMMY);
+ for (i = 0; i < NUM_INTRO_TICK_DUMMY; i++) {
+ writeByte(outFile, x_intro_dummy[i]);
+ writeByte(outFile, y_intro_dummy[i]);
+ }
+
+ writeUint16BE(outFile, NUM_INTRO_TICK_V3);
+ for (i = 0; i < NUM_INTRO_TICK_V3; i++) {
+ writeByte(outFile, x_intro_v3[i]);
+ writeByte(outFile, y_intro_v3[i]);
}
// Write textMouse
@@ -780,6 +819,29 @@ int main(int argc, char *argv[]) {
writeUint16BE(outFile, kALnewscr_2d);
writeUint16BE(outFile, 0);
+ // The following fonts info have been added to avoid temporarly the .FON
+ // used in the DOS version
+ // font5
+ nbrElem = sizeof(font5) / sizeof(byte);
+ writeUint16BE(outFile, nbrElem);
+
+ for (int j = 0; j < nbrElem; j++)
+ writeByte(outFile, font5[j]);
+
+ // font6
+ nbrElem = sizeof(font6) / sizeof(byte);
+ writeUint16BE(outFile, nbrElem);
+
+ for (int j = 0; j < nbrElem; j++)
+ writeByte(outFile, font6[j]);
+
+ // font8
+ nbrElem = sizeof(font8) / sizeof(byte);
+ writeUint16BE(outFile, nbrElem);
+
+ for (int j = 0; j < nbrElem; j++)
+ writeByte(outFile, font8[j]);
+
fclose(outFile);
return 0;
}
diff --git a/tools/create_hugo/create_hugo.h b/tools/create_hugo/create_hugo.h
index 671b5c9142..0215885c1d 100644
--- a/tools/create_hugo/create_hugo.h
+++ b/tools/create_hugo/create_hugo.h
@@ -31,7 +31,7 @@
#define DATAALIGNMENT 4
#define HUGO_DAT_VER_MAJ 0 // 1 byte
-#define HUGO_DAT_VER_MIN 19 // 1 byte
+#define HUGO_DAT_VER_MIN 25 // 1 byte
typedef unsigned char uint8;
typedef unsigned char byte;
diff --git a/tools/create_hugo/dists/msvc9/create_hugo.vcproj b/tools/create_hugo/dists/msvc9/create_hugo.vcproj
index f84c3d49d1..f6680e0280 100644
--- a/tools/create_hugo/dists/msvc9/create_hugo.vcproj
+++ b/tools/create_hugo/dists/msvc9/create_hugo.vcproj
@@ -194,6 +194,10 @@
>
</File>
<File
+ RelativePath="..\..\staticfont.h"
+ >
+ </File>
+ <File
RelativePath="..\..\staticintro.h"
>
</File>
diff --git a/tools/create_hugo/enums.h b/tools/create_hugo/enums.h
index eb03896fbd..a526c99ca5 100644
--- a/tools/create_hugo/enums.h
+++ b/tools/create_hugo/enums.h
@@ -741,7 +741,7 @@ enum seqActList_3d {
};
// Enumerate picture files. All screens must have an entry here, in order
-enum screeenid_1w {
+enum screenid_1w {
HOUSE_1w, HALL_1w, BED1_1w, DININGRM_1w, BATHROOM_1w, KITCHEN_1w,
GARDEN_1w, STORERM_1w, BASEMENT_1w, BATCAVE_1w, MUMMYRM_1w, LAKEROOM_1w,
DEADEND_1w, JAIL_1w, THE_END_1w, LAB_1w, FINTRO_1w, NUM_PICS_1w
@@ -785,7 +785,13 @@ enum screenid_3w {
};
// Hugo 1 DOS doesn't use a DAT file : the screen files are not packed together
-enum screenid_1d {NUM_PICS_1d};
+//enum screenid_1d {NUM_PICS_1d};
+enum screenid_1d {
+ HOUSE_1d, HALL_1d, BED1_1d, DININGRM_1d, BATHROOM_1d, KITCHEN_1d,
+ GARDEN_1d, STORERM_1d, BASEMENT_1d, BATCAVE_1d, MUMMYRM_1d, LAKEROOM_1d,
+ DEADEND_1d, JAIL_1d, THE_END_1d, LAB_1d, FINTRO_1d, NUM_PICS_1d
+};
+
enum screenid_2d {
/* 0*/ HOUSE_2d, HALL_2d, BED1_2d, BED2_2d, KEYHOLE_FILE_2d,
@@ -1006,6 +1012,7 @@ enum string_t_3w {
};
enum string_t_1d {
+ kSTdummy_1d,
//***************************************************************************
// Hugo 1 Dos - Not stored in a dat file!!!
//***************************************************************************
diff --git a/tools/create_hugo/staticdata.h b/tools/create_hugo/staticdata.h
index 880d67128f..55945c899e 100644
--- a/tools/create_hugo/staticdata.h
+++ b/tools/create_hugo/staticdata.h
@@ -1169,318 +1169,88 @@ const char **arrayNouns_3w[] = {
// Hugo 1 Dos
//***************************************************************************
-const char *nHero_1d[] = {"hero", "self", ""};
-const char *nTrap_1d[] = {"trap", ""};
-const char *nWard_1d[] = {"wardrobe", "closet", "cabinet", ""};
-const char *nDoor_1d[] = {"door", ""};
-const char *nBat_1d[] = {"bat", ""};
-const char *nEyes_1d[] = {"eyes", ""};
-const char *nPkin_1d[] = {"pumpkin", ""};
-const char *nCandle_1d[] = {"candle", ""};
-const char *nRope_1d[] = {"rope", "string", ""};
-const char *nCupb_1d[] = {"cupboard", "cabinet", "dresser", "hutch", "sideboard", "bureau", ""};
-const char *nKnife_1d[] = {"knife", "penknife", ""};
-const char *nWhistle_1d[] = {"whistle", ""};
-const char *nWdoorl_1d[] = {"wdoorl", ""};
-const char *nWdoorr_1d[] = {"wdoorr", ""};
-const char *nMask_1d[] = {"mask", ""};
-const char *nButler_1d[] = {"butler", "waiter", "servant", ""};
-const char *nChop_1d[] = {"chop", "meat", "steak", ""};
-const char *nRedeyes_1d[] = {"redeyes", ""};
-const char *nLips_1d[] = {"lips", ""};
-const char *nArm_1d[] = {"arm", ""};
-const char *nHdlshero_1d[] = {"hdlshero", ""};
-const char *nMonkey_1d[] = {"monkey", ""};
-const char *nKey_1d[] = {"key", ""};
-const char *nDog_1d[] = {"dog", ""};
-const char *nCarpet_1d[] = {"carpet", "rug", "mat", ""};
-const char *nBolt_1d[] = {"bolt", ""};
-const char *nHerodead_1d[] = {"herodead", ""};
-const char *nOilcan_1d[] = {" oil", ""};
-const char *nMummy_1d[] = {"mummy", ""};
-const char *nMdoor_1d[] = {"mdoor", ""};
-const char *nGold_1d[] = {"gold", "money", "treasure", "bag", "jewels", ""};
-const char *nBoat_1d[] = {"boat", ""};
-const char *nWhero_1d[] = {"whero", ""};
-const char *nOldman_1d[] = {"oldman", "old", "man", ""};
-const char *nGuard_1d[] = {"guard", "man", ""};
-const char *nProf_1d[] = {"prof", ""};
-const char *nIgor_1d[] = {"igor", "man", ""};
-const char *nBung_1d[] = {"bung", ""};
-const char *nGdoor_1d[] = {"glasdoor", "door", ""};
-const char *nSpachero_1d[] = {"spachero", ""};
-const char *nFuzyhero_1d[] = {"fuzyhero", ""};
const char *nArc_1d[] = {"arc", ""};
+const char *nHero_1d[] = {"hero", "self", "Hugo", ""};
+const char *nKey_1d[] = {"key", "a door key", " key", ""};
+const char *nMonkey_1d[] = {"monkey", "~", "Hugo", ""};
+const char *nOldman_1d[] = {"oldman", "old", "old man", "man", ""};
+const char *nPkin_1d[] = {"pumpkin", "a pumpkin", "pumpkin", ""};
+const char *nWard_1d[] = {"wardrobe", "cabinet", "closet", ""};
-const char *nTree_1d[] = {"tree", ""};
-const char *nFence_1d[] = {"fence", ""};
-const char *nPicture_1d[] = {"picture", "painting", ""};
-const char *nTable_1d[] = {"table", ""};
-const char *nBed_1d[] = {"bed", ""};
-const char *nPlant_1d[] = {"plant", "flower", "vase", ""};
-const char *nFood_1d[] = {"food", "plate", ""};
-const char *nMan_1d[] = {"man", "monster", "dracula", "frankenstein", "alien", "guest", "vampire", ""};
-const char *nMirror_1d[] = {"mirror", ""};
-const char *nToilet_1d[] = {"toilet", ""};
-const char *nBath_1d[] = {"bath", "tub", ""};
-const char *nUnits_1d[] = {"Unit", "oven", "sink", "cupb", "drawer", "pantry", "cabinet", "range", "counter", ""};
-const char *nBroom_1d[] = {"broom", ""};
-const char *nMousehole_1d[] = {"hole", ""};
-const char *nRock_1d[] = {"rock", "boulder", "stone", ""};
-const char *nTomb_1d[] = {"tomb", "coffin", "box", "sarcop", ""};
-const char *nWindow_1d[] = {"window", "outside", "inside", ""};
+const char *nMan_1d[] = {"man", "dracula", "monster", "frankenstein", "alien", "guest", "vampire", ""};
+const char *nPlant_1d[] = {"plant", "flower", "plant", "vase", ""};
const char **arrayNouns_1d[] = {
- nDummy, nHero_1d, nTrap_1d, nWard_1d, nDoor_1d,
- nBat_1d, nEyes_1d, nPkin_1d, nCandle_1d, nRope_1d,
- nCupb_1d, nKnife_1d, nWhistle_1d, nWdoorl_1d, nWdoorr_1d,
- nMask_1d, nButler_1d, nChop_1d, nRedeyes_1d, nLips_1d,
- nArm_1d, nHdlshero_1d, nMonkey_1d, nKey_1d, nShed_2w,
- nDog_1d, nCarpet_1d, nBolt_1d, nHerodead_1d, nOilcan_1d,
- nMummy_1d, nMdoor_1d, nGold_1d, nBoat_1d, nWhero_1d,
- nOldman_1d, nGuard_1d, nProf_1d, nIgor_1d, nBung_1d,
- nGdoor_1d, nSpachero_1d, nFuzyhero_1d, nArc_1d, nSky_1w,
- nWall_1w, nGround_1w, nTree_1d, nFence_1d, nHouse_1w,
- nRoof_1w, nLight_1w, nMoon_1w, nPicture_1d, nTable_1d,
- nStairs_1w, nBed_1d, nFace_1w, nPlant_1d, nWitch_1w,
- nFood_1d, nWoman_1w, nMan_1d, nMirror_1d, nToilet_1d,
- nBath_1d, nSink_1w, nUnits_1d, nBroom_1d, nGardenbits_1w,
- nMousehole_1d, nPenelope_1w, nRock_1d, nTomb_1d, nDroppings_1w,
- nMachinebits_1w, nHands_1w, nWindow_1d
+ nDummy, nHero_1d, nTrap_1w, nWard_1d, nDoor_1w,
+ nBat_1w, nEyes_1w, nPkin_1d, nCandle_1w, nRope_1w,
+ nCupb_1w, nKnife_1w, nWhistle_1w, nWdoorl_1w, nWdoorr_1w,
+ nMask_1w, nButler_1w, nChop_1w, nRedeyes_1w, nLips_1w,
+ nArm_1w, nHdlshero_1w, nMonkey_1d, nKey_1d, nShed_2w,
+ nDog_1w, nCarpet_1w, nBolt_1w, nHerodead_1w, nOilcan_1w,
+ nMummy_1w, nMdoor_1w, nGold_1w, nBoat_1w, nWhero_1w,
+ nOldman_1d, nGuard_1w, nProf_1w, nIgor_1w, nBung_1w,
+ nGdoor_1w, nSpachero_1w, nFuzyhero_1w, nArc_1d, nSky_1w,
+ nWall_1w, nGround_1w, nTree_1w, nFence_1w, nHouse_1w,
+ nRoof_1w, nLight_1w, nMoon_1w, nPicture_1w, nTable_1w,
+ nStairs_1w, nBed_1w, nFace_1w, nPlant_1d, nWitch_1w,
+ nFood_1w, nWoman_1w, nMan_1d, nMirror_1w, nToilet_1w,
+ nBath_1w, nSink_1w, nUnits_1w, nBroom_1w, nGardenbits_1w,
+ nMousehole_1w, nPenelope_1w, nRock_1w, nTomb_1w, nDroppings_1w,
+ nMachinebits_1w, nHands_1w, nWindow_1w
};
//***************************************************************************
// Hugo 2 Dos
//***************************************************************************
-const char *nHero_2d[] = {"hero", "self", ""};
-const char *nPenny_2d[] = {"penelope", "woman", "girl", "lady", ""};
-const char *nPennylie_2d[] = {"pennylie", ""};
-const char *nPenfall_2d[] = {"penfall", ""};
-const char *nSmoke_2d[] = {"smoke", ""};
-const char *nLips_2d[] = {"lips", ""};
-const char *nMaid_2d[] = {"maid", "waitress", "woman", "girl", "lady", ""};
-const char *nBookcase_2d[] = {"bookcase", ""};
-const char *nBook_2d[] = {"book", ""};
-const char *nKeyhole_2d[] = {"hole", ""};
-const char *nPanel_2d[] = {"panel", ""};
-const char *nMatches_2d[] = {"matches", "a box of matches", "match", ""};
-const char *nCrate_2d[] = {"crate", "box", ""};
-const char *nDumb_2d[] = {"dumb", "waiter", "box", ""};
-const char *nMurder_2d[] = {"murder", ""};
-const char *nTrap_2d[] = {"trap", ""};
-const char *nWard_2d[] = {"wardrobe", "closet", "cabinet", ""};
-const char *nDoor_2d[] = {"door", "gate", ""};
-const char *nRope_2d[] = {"rope", "string", ""};
+const char *nBug_2d[] = {"bug", "bee", "bug zapper", "insect", "wasp", ""};
const char *nCupb_2d[] = {"cupboard", "cabinet", "dresser", "hutch", "sideboard", "bureau", "desk", "drawer", ""};
-const char *nGarlic_2d[] = {"garlic", "some garlic", "clove", ""};
-const char *nGardner_2d[] = {"gard", "man", ""};
-const char *nButton_2d[] = {"button", "switch", "knob", ""};
-const char *nRed_2d[] = {"red", ""};
-const char *nYellow_2d[] = {"yellow", ""};
-const char *nGreen_2d[] = {"green", ""};
-const char *nBlue_2d[] = {"blue", ""};
-const char *nFly_2d[] = {"fly", ""};
-const char *nLeaf_2d[] = {"leaf", ""};
-const char *nShedlight_2d[] = {"slight", ""};
-const char *nGatelight_2d[] = {"glight", ""};
-const char *nZapper_2d[] = {"zapper", "lamp", "light", "lantern", ""};
-const char *nBug_2d[] = {"bug", "bee", "insect", "wasp", ""};
-const char *nKnife_2d[] = {"knife", ""};
-const char *nShed_2d[] = {"shed", ""};
-const char *nOldman_2d[] = {"oldman", "old", "man", ""};
-const char *nSnake_2d[] = {"snake", ""};
-const char *nStick_2d[] = {"stick", "a pile of sticks", "twig", ""};
-const char *nDynamite_2d[] = {"dynamite", "a stick of dynamite", "fuze", "fuse", ""};
-const char *nKennel_2d[] = {"kennel", "house", ""};
-const char *nDog_2d[] = {"dog", ""};
-const char *nWell_2d[] = {"well", "rope", ""};
-const char *nBanana_2d[] = {"banana", "a moldy banana", ""};
-const char *nLamp_2d[] = {"lamp", "an old dusty oil lamp", ""};
-const char *nGenie_2d[] = {"genie", "man", ""};
-const char *nTardis_2d[] = {"tardis", "phone", ""};
-const char *nHarry_2d[] = {"harry", "man", ""};
-const char *nHester_2d[] = {"hester", "woman", "girl", "lady", ""};
-const char *nLetter_2d[] = {"letter", "envelope", "card", ""};
-const char *nDoctor_2d[] = {"doctor", "man", ""};
-const char *nDalek_2d[] = {"alek", "robot", "monster", ""};
-const char *nCook_2d[] = {"cook", "woman", "girl", "lady", ""};
-const char *nCookb_2d[] = {"cookb", ""};
-const char *nCop_2d[] = {"cop", "man", ""};
-const char *nHorace_2d[] = {"horace", "man", ""};
-const char *nBell_2d[] = {"bell", "a bell", ""};
-const char *nCatnip_2d[] = {"catnip", "some catnip", ""};
-const char *nCat_2d[] = {"cat", "puss", ""};
-const char *nGun_2d[] = {"gun", "a gun", "revolver", "pistol", "weapon", ""};
-const char *nPaper_2d[] = {"paper", "a newspaper", "notepad", " pad", "blotter", ""};
-const char *nPencil_2d[] = {"pencil", "a pencil", " pen", ""};
-const char *nKey_2d[] = {"key", ""};
-const char *nMagnify_2d[] = {"magnifier", "a magnifying glass", "magnify", "glass", ""};
-const char *nSafe_2d[] = {"safe", ""};
-const char *nScrew_2d[] = {"screwdriver", "sonic", ""};
-const char *nWill_2d[] = {"will", "print", ""};
-const char *nAlbum_2d[] = {"album", "photo", ""};
-const char *nBottle_2d[] = {"bottle", "a bottle", "serum", ""};
-const char *nBalloon_2d[] = {"balloon", ""};
-
-const char *nSky_2d[] = {"sky", "ceiling", ""};
-const char *nWall_2d[] = {"wall", ""};
-const char *nGround_2d[] = {"ground", "floor", ""};
-const char *nTree_2d[] = {"tree", ""};
-const char *nFence_2d[] = {"fence", "hedge", "bush", ""};
-const char *nHouse_2d[] = {"house", ""};
-const char *nRoof_2d[] = {"roof", "chimney", ""};
-const char *nLight_2d[] = {"light", "lamp", ""};
-const char *nMoon_2d[] = {"moon", ""};
-const char *nPicture_2d[] = {"picture", "painting", ""};
-const char *nTable_2d[] = {"table", "bench", ""};
-const char *nStairs_2d[] = {"stair", "landing", "ladder", ""};
-const char *nBed_2d[] = {"bed", ""};
-const char *nPlant_2d[] = {"plant", "flower", "vase", ""};
-const char *nFood_2d[] = {"food", "plate", ""};
-const char *nWoman_2d[] = {"woman", "girl", "lady", ""};
-const char *nMan_2d[] = {"man", ""};
-const char *nMirror_2d[] = {"mirror", ""};
-const char *nSink_2d[] = {"sink", ""};
-const char *nUnits_2d[] = {"Unit", "oven", "sink", "cupb", "drawer", "pantry", "cabinet", "range", "counter", "cooker", ""};
-const char *nBroom_2d[] = {"broom", "brush", ""};
-const char *nGardenbits_2d[] = {"grass", "fence", "Branch", "path", ""};
-const char *nRock_2d[] = {"rock", "boulder", "stone", ""};
-const char *nDroppings_2d[] = {"droppings", ""};
-const char *nWindow_2d[] = {"window", "outside", "inside", ""};
-const char *nBird_2d[] = {"bird", "parrot", "budgie", ""};
-const char *nCage_2d[] = {"cage", "pen", ""};
-const char *nPhone_2d[] = {"phone", ""};
-const char *nChair_2d[] = {"chair", "seat", "couch", "settee", ""};
-const char *nTools_2d[] = {"tools", "hammer", "pliers", "screw", "knife", "saw", ""};
-const char *nBridge_2d[] = {"bridge", ""};
-const char *nWater_2d[] = {"water", ""};
-const char *nBucket_2d[] = {"bucket", "container", ""};
-const char *nMouse_2d[] = {"mouse", "rat", "rodent", "animal", "furry", ""};
-const char *nWand_2d[] = {"wand", ""};
-const char *nHole_2d[] = {"hole", ""};
-const char *nHandle_2d[] = {"handle", "lever", ""};
-const char *nChute_2d[] = {"chute", ""};
-const char *nOrgan_2d[] = {"organ", "piano", "pipe", ""};
-const char *nPost_2d[] = {"post", "pole", ""};
-const char *nGraf_2d[] = {"graffiti", "graf", "writing", ""};
+const char *nDalek_2d[] = {"alek", "monster", "robot", ""};
const char **arrayNouns_2d[] = {
- nDummy, nHero_2d, nPenny_2d, nPennylie_2d, nPenfall_2d,
- nSmoke_2d, nLips_2d, nMaid_2d, nBookcase_2d, nBook_2d,
- nKeyhole_2d, nPanel_2d, nMatches_2d, nCrate_2d, nDumb_2d,
- nMurder_2d, nTrap_2d, nWard_2d, nDoor_2d, nRope_2d,
- nCupb_2d, nGarlic_2d, nGardner_2d, nButton_2d, nRed_2d,
- nYellow_2d, nGreen_2d, nBlue_2d, nFly_2d, nLeaf_2d,
- nShedlight_2d, nGatelight_2d, nZapper_2d, nBug_2d, nKnife_2d,
- nShed_2d, nOldman_2d, nSnake_2d, nStick_2d, nDynamite_2d,
- nKennel_2d, nDog_2d, nWell_2d, nBanana_2d, nLamp_2d,
- nGenie_2d, nTardis_2d, nHarry_2d, nHester_2d, nLetter_2d,
- nDoctor_2d, nDalek_2d, nCook_2d, nCookb_2d, nCop_2d,
- nHorace_2d, nBell_2d, nCatnip_2d, nCat_2d, nGun_2d,
- nPaper_2d, nPencil_2d, nKey_2d, nMagnify_2d, nSafe_2d,
- nScrew_2d, nWill_2d, nAlbum_2d, nBottle_2d, nBalloon_2d,
- nSky_2d, nWall_2d, nGround_2d, nTree_2d, nFence_2d,
- nHouse_2d, nRoof_2d, nLight_2d, nMoon_2d, nPicture_2d,
- nTable_2d, nStairs_2d, nBed_2d, nPlant_2d, nFood_2d,
- nWoman_2d, nMan_2d, nMirror_2d, nSink_2d, nUnits_2d,
- nBroom_2d, nGardenbits_2d, nRock_2d, nDroppings_2d, nWindow_2d,
- nBird_2d, nCage_2d, nPhone_2d, nChair_2d, nTools_2d,
- nBridge_2d, nWater_2d, nBucket_2d, nMouse_2d, nWand_2d,
- nHole_2d, nHandle_2d, nChute_2d, nOrgan_2d, nPost_2d,
- nGraf_2d
-};
-
-const char *nHero_3d[] = {"hero", "self", ""};
-const char *nWhero_3d[] = {"whero", ""};
-const char *nHero_old_3d[] = {"hero_old", ""};
-const char *nWheroold_3d[] = {"wheroold", ""};
-const char *nPenny_3d[] = {"penelope", "woman", "girl", "lady", ""};
-const char *nPennylie_3d[] = {"pennylie", ""};
-const char *nLips_3d[] = {"lips", ""};
-const char *nPlane_3d[] = {"plane", "craft", "cabin", "cockpit", ""};
-const char *nDoor_3d[] = {"door", ""};
-const char *nCdoor_3d[] = {"door", "cage", "gate", ""};
-const char *nBlock_3d[] = {"block", ""};
-const char *nVine_3d[] = {"vine", "rope", ""};
-const char *nSwinger_3d[] = {"swinger", ""};
-const char *nDoctor_3d[] = {"doctor", "witch", "native", "man", ""};
-const char *nClay_3d[] = {"clay", "some clay", "plasticine", "model", "effigy", "voodoo", "doll", ""};
-const char *nDoorlock_3d[] = {"lock", ""};
-const char *nNeedles_3d[] = {"needle", "some pins", "pin", ""};
-const char *nNative_3d[] = {"native", "man", "lady", ""};
-const char *nNat1_3d[] = {"nat1", "native", "man", ""};
-const char *nNat2_3d[] = {"nat2", "native", "man", ""};
-const char *nNat3_3d[] = {"nat3", "native", "man", ""};
-const char *nNatb_3d[] = {"natb", "native", "man", ""};
-const char *nNatg_3d[] = {"natg", "native", "man", "girl", ""};
-const char *nBottles_3d[] = {"bottles", ""};
-const char *nFlask_3d[] = {"flask", "a water flask", "water", "remedy", "antidote", ""};
-const char *nCage_3d[] = {"cage", "a little cage", "door", ""};
-const char *nPipe_3d[] = {"blowpipe", "blowpipe & darts", "dart", ""};
-const char *nElephant_3d[] = {"elephant", "animal", ""};
-const char *nE_eyes_3d[] = {"e_eyes", ""};
-const char *nBouillon_3d[] = {"bouillon", "bouillon cubes", "bouil", "boull", "boulion", "cube", "season", "oxo", "knorr", ""};
-const char *nMoushole_3d[] = {"moushole", ""};
-const char *nDoclie_3d[] = {"doclie", ""};
-const char *nCheese_3d[] = {"cheese", "a sandwich", "sandwich", ""};
-const char *nSpider_3d[] = {"spider", "insect", "tarantula", ""};
-const char *nSnake_3d[] = {"snake", ""};
-const char *nFire_3d[] = {"fire", "flame", "pot", ""};
+ nDummy, nHero_2w, nPenny_2w, nPennylie_2w, nPenfall_2w,
+ nSmoke_2w, nLips_1w, nMaid_2w, nBookcase_2w, nBook_2w,
+ nKeyhole_2w, nPanel_2w, nMatches_2w, nCrate_2w, nDumb_2w,
+ nMurder_2w, nTrap_2w, nWard_2w, nDoor_2w, nRope_2w,
+ nCupb_2d, nGarlic_2w, nGardner_2w, nButton_2w, nRed_2w,
+ nYellow_2w, nGreen_2w, nBlue_2w, nFly_2w, nLeaf_2w,
+ nShedlight_2w, nGatelight_2w, nZapper_2w, nBug_2d, nKnife_2w,
+ nShed_2w, nOldman_2w, nSnake_2w, nStick_2w, nDynamite_2w,
+ nKennel_2w, nDog_2w, nWell_2w, nBanana_2w, nLamp_2w,
+ nGenie_2w, nTardis_2w, nHarry_2w, nHester_2w, nLetter_2w,
+ nDoctor_2w, nDalek_2d, nCook_2w, nCookb_2w, nCop_2w,
+ nHorace_2w, nBell_2w, nCatnip_2w, nCat_2w, nGun_2w,
+ nPaper_2w, nPencil_2w, nKey_2w, nMagnify_2w, nSafe_2w,
+ nScrew_2w, nWill_2w, nAlbum_2w, nBottle_2w, nBalloon_2w,
+ nSky_1w, nWall_1w, nGround_1w, nTree_2w, nFence_2w,
+ nHouse_1w, nRoof_1w, nLight_1w, nMoon_1w, nPicture_1w,
+ nTable_2w, nStairs_2w, nBed_2w, nPlant_2w, nFood_2w,
+ nWoman_1w, nMan_2w, nMirror_1w, nSink_1w, nUnits_2w,
+ nBroom_2w, nGardenbits_1w, nRock_2w, nDroppings_1w, nWindow_1w,
+ nBird_2w, nCage_2w, nPhone_2w, nChair_2w, nTools_2w,
+ nBridge_2w, nWater_2w, nBucket_2w, nMouse_2w, nWand_2w,
+ nHole_2w, nHandle_2w, nChute_2w, nOrgan_2w, nPost_2w,
+ nGraf_2w
+};
+
const char *nDocbits_3d[] = {"skull", "shield", "torch", "fire", "flame", "spear", "bone", ""};
-const char *nFire_1_3d[] = {"fire_1", ""};
-const char *nFire_2_3d[] = {"fire_2", ""};
-const char *nFire_3_3d[] = {"fire_3", ""};
-const char *nFroth_3d[] = {"froth", ""};
-const char *nScroll_3d[] = {"scroll", "an old scroll", "writing", ""};
-const char *nCrystal_3d[] = {"crystal", "a crystal ball", "ball", ""};
-const char *nGhost_3d[] = {"ghost", "ghoul", "spirit", ""};
-const char *nBell_3d[] = {"bell", "a golden bell", ""};
-const char *nBook_3d[] = {"book", "a spell book", "spell", ""};
-const char *nCandle_3d[] = {"candle", "a golden candle", ""};
-
-const char *nSky_3d[] = {"sky", "ceiling", ""};
-const char *nWall_3d[] = {"wall", ""};
-const char *nGround_3d[] = {"ground", "floor", ""};
-const char *nFence_3d[] = {"fence", "hedge", "bush", ""};
-const char *nBridge_3d[] = {"bridge", ""};
-const char *nWater_3d[] = {"water", "stream", "river", "pool", "lake", ""};
-const char *nMouse_3d[] = {"mouse", "rat", "rodent", "animal", "furry", ""};
-const char *nWindow_3d[] = {"window", ""};
-const char *nShelfbits_3d[]= {"shelf", "bottle", "jar", "medicine", "label", ""};
-const char *nJungle_3d[] = {"tree", "vine", "flower", "fern", "frond", "plant", "jungle", "undergrowth", ""};
-const char *nOrchid_3d[] = {"orchid", "flower", "plant", ""};
-const char *nPole_3d[] = {"pole", "stick", "wood", ""};
-const char *nHut_3d[] = {"hut", "house", "shed", ""};
-const char *nRock_3d[] = {"rock", "boulder", "stone", ""};
-const char *nAircraft_3d[] = {"aircraft", ""};
-const char *nPlant1_3d[] = {"plant1", ""};
-const char *nPlant2_3d[] = {"plant2", ""};
-const char *nPlant3_3d[] = {"plant3", ""};
-const char *nPlant4_3d[] = {"plant4", ""};
-const char *nPlant5_3d[] = {"plant5", ""};
-const char *nWeb_3d[] = {"web", ""};
-const char *nO_eye_3d[] = {"o_eye", ""};
-const char *nFire_4_3d[] = {"fire_4", ""};
-const char *nMouth_3d[] = {"mouth", ""};
-const char *nFood_3d[] = {"food", "meat", "hyena", ""};
-const char *nThem_3d[] = {"them", ""};
+const char *nPipe_3d[] = {"blowpipe", "blowpipe & darts", "blowpipe", "dart", ""};
const char **arrayNouns_3d[] = {
- nDummy, nHero_3d, nWhero_3d, nHero_old_3d, nWheroold_3d,
- nPenny_3d, nPennylie_3d, nLips_3d, nPlane_3d, nDoor_3d,
- nCdoor_3d, nBlock_3d, nVine_3d, nSwinger_3d, nDoctor_3d,
- nClay_3d, nDoorlock_3d, nNeedles_3d, nNative_3d, nNat1_3d,
- nNat2_3d, nNat3_3d, nNatb_3d, nNatg_3d, nBottles_3d,
- nFlask_3d, nCage_3d, nPipe_3d, nElephant_3d, nE_eyes_3d,
- nBouillon_3d, nMoushole_3d, nDoclie_3d, nCheese_3d, nSpider_3d,
- nSnake_3d, nFire_3d, nDocbits_3d, nFire_1_3d, nFire_2_3d,
- nFire_3_3d, nFroth_3d, nScroll_3d, nCrystal_3d, nGhost_3d,
- nBell_3d, nBook_3d, nCandle_3d, nSky_3d, nWall_3d,
- nGround_3d, nFence_3d, nBridge_3d, nWater_3d, nMouse_3d,
- nWindow_3d, nShelfbits_3d, nJungle_3d, nOrchid_3d, nPole_3d,
- nHut_3d, nRock_3d, nAircraft_3d, nPlant1_3d, nPlant2_3d,
- nPlant3_3d, nPlant4_3d, nPlant5_3d, nWeb_3d, nO_eye_3d,
- nFire_4_3d, nMouth_3d, nFood_3d, nThem_3d
+ nDummy, nHero_2w, nWhero_1w, nHero_old_3w, nWheroold_3w,
+ nPenny_2w, nPennylie_2w, nLips_1w, nPlane_3w, nDoor_3w,
+ nCdoor_3w, nBlock_3w, nVine_3w, nSwinger_3w, nDoctor_3w,
+ nClay_3w, nDoorlock_3w, nNeedles_3w, nNative_3w, nNat1_3w,
+ nNat2_3w, nNat3_3w, nNatb_3w, nNatg_3w, nBottles_3w,
+ nFlask_3w, nCage_3w, nPipe_3d, nElephant_3w, nE_eyes_3w,
+ nBouillon_3w, nMoushole_3w, nDoclie_3w, nCheese_3w, nSpider_3w,
+ nSnake_3w, nFire_3w, nDocbits_3d, nFire_1_3w, nFire_2_3w,
+ nFire_3_3w, nFroth_3w, nScroll_3w, nCrystal_3w, nGhost_3w,
+ nBell_3w, nBook_3w, nCandle_3w, nSky_1w, nWall_1w,
+ nGround_1w, nFence_2w, nBridge_3w, nWater_3w, nMouse_3w,
+ nWindow_3w, nShelfbits_3w, nJungle_3w, nOrchid_3w, nPole_3w,
+ nHut_3w, nRock_3w, nAircraft_3w, nPlant1_3w, nPlant2_3w,
+ nPlant3_3w, nPlant4_3w, nPlant5_3w, nWeb_3w, nO_eye_3w,
+ nFire_4_3w, nMouth_3w, nFood_3w, nThem_3w
};
// Verbs and synonyms (N.B. put null string at end of last synonym)
@@ -1573,17 +1343,17 @@ const char *vBolt_1w[] = {"bolt", "~", "bolt", ""};
const char *vHero_1w[] = {"hero", "self", "Hugo", "hugo", ""};
const char **arrayVerbs_1w[] = {
- vDummy, vMakeUseOf_1w, vMagic_1w, vOpen_1w, vClose_1w,
- vUnlock_1w, vLock_1w, vPush_1w, vGive_1w, vRude_1w,
- vOff_1w, vInto_1w, vOutof_1w, vCrap_1w, vRide_1w,
- vTake_1w, vDrop_1w, vAttack_1w, vBreak_1w, vThrowit_1w,
- vWear_1w, vRub_1w, vOil_1w, vMove_1w, vLift_1w,
- vDig_1w, vUnder_1w, vLook_1w, vEat_1w, vBlow_1w,
- vUntie_1w, vCut_1w, vTalk_1w, vPlug_1w, vShout_1w,
- vQuery_1w, vJump_1w, vGo_1w, vEnter_1w, vClimb_1w,
- vSwitch_1w, vListen_1w, vKnock_1w, vSmell_1w, vSit_1w,
- vKiss_1w, vUnbolt_1w, vLakeverbs_1w, vHelp_1w, vDrink_1w,
- vSweep_1w, vFeed_1w, vWash_1w, vStroke_1w, vHide_1w,
+ vDummy, vMakeUseOf_1w, vMagic_1w, vOpen_1w, vClose_1w,
+ vUnlock_1w, vLock_1w, vPush_1w, vGive_1w, vRude_1w,
+ vOff_1w, vInto_1w, vOutof_1w, vCrap_1w, vRide_1w,
+ vTake_1w, vDrop_1w, vAttack_1w, vBreak_1w, vThrowit_1w,
+ vWear_1w, vRub_1w, vOil_1w, vMove_1w, vLift_1w,
+ vDig_1w, vUnder_1w, vLook_1w, vEat_1w, vBlow_1w,
+ vUntie_1w, vCut_1w, vTalk_1w, vPlug_1w, vShout_1w,
+ vQuery_1w, vJump_1w, vGo_1w, vEnter_1w, vClimb_1w,
+ vSwitch_1w, vListen_1w, vKnock_1w, vSmell_1w, vSit_1w,
+ vKiss_1w, vUnbolt_1w, vLakeverbs_1w, vHelp_1w, vDrink_1w,
+ vSweep_1w, vFeed_1w, vWash_1w, vStroke_1w, vHide_1w,
vBolt_1w, vHero_1w
};
@@ -1599,7 +1369,7 @@ const char *vFire_2w[] = {"fire", "shoot", ""};
const char *vSwitch_2w[] = {"switch", "~", "switch", ""};
const char *vHello_2w[] = {"hello", "hi ", " hi", ""};
-const char *vRude_2w[] = {"fuck", "fart", "screw ", ""};// Must come before off
+const char *vRude_2w[] = {"fuck", "fart", "screw ", ""};// Must come before off
const char *vScribble_2w[] = {"scribble", ""};
const char **arrayVerbs_2w[] = {
@@ -1643,7 +1413,7 @@ const char *vDouse_3w[] = {"douse", "extinguish", "put out", ""};
const char *vExorcise_3w[] = {"exorcise", "banish", "frighten", "scare", ""};
const char *vHello_3w[] = {"hello", "hi ", " hi", "howd", ""};
-const char *vRude_3w[] = {"fuck", "fart", "screw ", "shit", ""}; // Must come before off
+const char *vRude_3w[] = {"fuck", "fart", "screw ", "shit", ""}; // Must come before off
const char *vNaughty_3w[] = {"fondle", "breasts", "tits", "fanny", "kiss", ""};
const char *vPut_3w[] = {"put ", ""};
const char *vSwim_3w[] = {"swim", "wade", ""};
@@ -1765,7 +1535,7 @@ const char *vWind_2d[] = {"wind", "turn", "rotate", "winch", ""}; // As in
const char *vTie_2d[] = {"tie ", "attach", ""};
const char *vStroke_2d[] = {"stroke", "pet ", ""};
const char *vUnscrew_2d[] = {"unscrew", ""}; // Must come before rude
-const char *vRude_2d[] = {"fuck", "fart", "screw ", ""}; // Must come before off
+const char *vRude_2d[] = {"fuck", "fart", "screw ", ""}; // Must come before off
const char *vUndress_2d[] = {"undress", ""}; // Must come before take
const char *vPlay_2d[] = {"play", ""};
const char *vWish_2d[] = {"wish", ""};
@@ -1930,7 +1700,7 @@ const char *screenNames_1d[] = {
"House", "Hall", "Bed1", "Diningrm", "Bathroom",
/* 5*/ "Kitchen", "Garden", "Storerm", "Basement", "Batcave",
/*10*/ "Mummyrm", "Lakeroom", "Deadend", "Jail", "The_end",
-/*15*/ "Lab"
+/*15*/ "Lab", "House"
};
const char *screenNames_2d[] = {
@@ -3520,7 +3290,7 @@ background_t catchall_1d[] = { // Generally applicable phrases
{kVRude_1d, 0, kSTnorude_1d, false, 0, 0},
{kVKnock_1d, 0, kSTnoknock_1d, false, 0, 0},
{kVTake_1d, kNPicture_1d, kSTnopurps_1d, false, 0, 0},
- {0, 0, 0, false, 0, 0}
+ {0, 0, 0, false, 0, 0}
};
background_t screen0_desc_1d[] = { // Outside house
@@ -3912,7 +3682,7 @@ background_t screen10_desc_2d[] = { // Venus fly traps
{0, 0, 0, false, 0, 0}
};
-background_t screen11_desc_2d[] = { // Gates kVOpen_2d,
+background_t screen11_desc_2d[] = { // Gates kVOpen_2d,
{kVLook_2d, 0, kSTS11look_2d, true, 0, 0},
{kVClose_2d, kNDoor_2d, kSTNopurps_2d, false, 0, 0},
{0, 0, 0, false, 0, 0}
@@ -4512,70 +4282,70 @@ byte points_3d[] = {
12, 2
};
-cmd blowdw_1w = {kVBlow_1w, 0, kDTnull, 0, 0, kDTnull, kDTokblow_1w, kALblowdw_1w};
-cmd breakpkin_1w = {kVBreak_1w, 0, kDTnull, 0, 1, kDTnull, kDTokgen_1w, kALpkin_1w};
-cmd brkrope_1w = {kVBreak_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsrbreak_1w, 0};
-cmd closebolt_1w = {kVClose_1w, 0, kDTnull, 2, 1, kDTsclosed_1w, kDTsclosebolt_1w, 0};
-cmd closedoor1_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosedoor1_1w};
-cmd closedoor2_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, 0};
-cmd closedoor3_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, 0};
-cmd closedoor4_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosedoor4_1w};
-cmd closetrap_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosetrap_1w};
-cmd closewdoors_1w = {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosewdoors_1w};
-cmd cutrope_1w = {kVCut_1w, kRknife_1w, kDTrnoknife_1w, 0, 1, kDTsnocut_1w, kDTscut_1w, kALcutrope_1w};
-cmd dropmask_1w = {kVDrop_1w, 0, kDTnull, 0, 0, kDTsworn3_1w, kDTnull, kALdropmask_1w};
-cmd droppkin_1w = {kVDrop_1w, kRpkin_1w, kDTnocgen_1w, 0, 1, kDTnull, kDTnull, kALpkin_1w};
-cmd eatchop_1w = {kVEat_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALeatchop2_1w};
-cmd getchop_1w = {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALgetchop_1w};
-cmd getdw_1w = {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALcupbdw_1w};
-cmd getinboat_1w = {kVInto_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetinboat_1w};
-cmd getknife_1w = {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALcupbpk_1w};
-cmd getoilcan_1w = {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALshedoil_1w};
-cmd getoutboat_1w = {kVOutof_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetoutboat_1w};
-cmd givegold_1w = {kVGive_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALgold_1w};
-cmd hiderock_1w = {kVHide_1w, 0, kDTnull, 0, 1, kDTsrock_1w, kDTnull, kALrock_1w};
-cmd kickpkin_1w = {kVAttack_1w, 0, kDTnull, 0, 1, kDTnull, kDTokgen_1w, kALpkin_1w};
-cmd knock_1w = {kVKnock_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_1w, 0};
-cmd lockbolt_1w = {kVLock_1w, 0, kDTnull, 2, 1, kDTsclosed_1w, kDTsclosebolt_1w, 0};
-cmd lockdoor_1w = {kVLock_1w, kRkey_1w, kDTnockey_1w, 1, 0, kDTslock_1w, kDTokgen_1w, kALclosedoor1_1w};
-cmd lookcupb_1w = {kVLook_1w, kRcandle_1w, kDTnoccandle_1w, DONT_CARE, 0, kDTnull, kDTnull, kALlookcupb_1w};
-cmd lookshed_1w = {kVLook_1w, kRcandle_1w, kDTnoccandle_1w, 0, 0, kDTsnoseeoil_1w, kDTsseeoil_1w, kALshedoil_1w};
-cmd movecarp1_1w = {kVMove_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w};
-cmd movecarp2_1w = {kVLift_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w};
-cmd movecarp3_1w = {kVUnder_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w};
-cmd offmask_1w = {kVOff_1w, 0, kDTnull, 1, 0, kDTsworn2_1w, kDTokgen_1w, kALswapmask_1w};
-cmd oilbolt_1w = {kVOil_1w, kRoil_1w, kDTrnooil_1w, 0, 1, kDTsoiled_1w, kDTsoilbolt_1w, 0};
-cmd omattack_1w = {kVAttack_1w, 0, kDTnull, 0, 0, kDTnull, kDTsomattack_1w, 0};
-cmd ombreak_1w = {kVBreak_1w, 0, kDTnull, 0, 0, kDTnull, kDTsomattack_1w, 0};
-cmd omtalk_1w = {kVTalk_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALoldman_1w};
-cmd openbolt_1w = {kVOpen_1w, 0, kDTnull, 1, 2, kDTsstuck_1w, kDTsopenbolt_1w, 0};
-cmd opendoor1_1w = {kVOpen_1w, kRkey_1w, kDTslocked_1w, 0, 1, kDTsopen_1w, kDTsunlock_1w, kALopendoor1_1w};
-cmd opendoor2_1w = {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopendoor2_1w};
-cmd opendoor3_1w = {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopendoor3_1w};
-cmd opendoor4_1w = {kVOpen_1w, 0, kDTnull, 0, 0, kDTsopen_1w, kDTnull, kALopendoor4_1w};
-cmd openpkin_1w = {kVOpen_1w, 0, kDTnull, 0, 1, kDTnull, kDTsopenpkin_1w, kALpkin_1w};
-cmd opentrap_1w = {kVOpen_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALopentrap_1w};
-cmd openwdoors_1w = {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopenwdoors_1w};
-cmd plugbung_1w = {kVPlug_1w, kRbung_1w, kDTnocgen_1w, 0, 1, kDTnull, kDTsplug_1w, kALplugbung_1w};
-cmd pushboat_1w = {kVPush_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALpushboat_1w};
-cmd pushigor_1w = {kVPush_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALigor_1w};
-cmd ruboilcan_1w = {kVRub_1w, 0, kDTnull, 0, 0, kDTnull, kDTsruboil_1w, 0};
-cmd talkdrac_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkdrac_1w};
-cmd talkfrank_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkfrank_1w};
-cmd talkgwen_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkgwen_1w};
-cmd talkhood_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkhood_1w};
-cmd talkpeahd_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkpeahd_1w};
-cmd talkslime_1w = {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkslime_1w};
-cmd throwchop_1w = {kVThrowit_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALthrowchop_1w};
-cmd unlkdoor_1w = {kVUnlock_1w, kRkey_1w, kDTnockey_1w, 0, 1, kDTsunlocked_1w, kDTsunlock_1w, kALopendoor1_1w};
-cmd unlock_1w = {kVUnlock_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_1w, 0};
-cmd unlockbolt_1w = {kVUnlock_1w, 0, kDTnull, 1, 2, kDTsstuck_1w, kDTsopenbolt_1w, 0};
-cmd untierope_1w = {kVUntie_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsuntie_1w, 0};
-cmd useboat_1w = {kVMakeUseOf_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALuseboat_1w};
-cmd usemask_1w = {kVMakeUseOf_1w, kRmask_1w, kDTnocgen_1w, DONT_CARE, 0, kDTnull, kDTnull, kALusemask_1w};
-cmd wearmask_1w = {kVWear_1w, kRmask_1w, kDTnocgen_1w, 0, 1, kDTsworn1_1w, kDTokgen_1w, kALswapmask_1w};
-
-cmd emptyCmd = {0, 0, kDTnull, 0, 0, kDTnull, kDTnull, 0};
+#define blowdw_1w {kVBlow_1w, 0, kDTnull, 0, 0, kDTnull, kDTokblow_1w, kALblowdw_1w}
+#define breakpkin_1w {kVBreak_1w, 0, kDTnull, 0, 1, kDTnull, kDTokgen_1w, kALpkin_1w}
+#define brkrope_1w {kVBreak_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsrbreak_1w, 0}
+#define closebolt_1w {kVClose_1w, 0, kDTnull, 2, 1, kDTsclosed_1w, kDTsclosebolt_1w, 0}
+#define closedoor1_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosedoor1_1w}
+#define closedoor2_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, 0}
+#define closedoor3_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, 0}
+#define closedoor4_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosedoor4_1w}
+#define closetrap_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosetrap_1w}
+#define closewdoors_1w {kVClose_1w, 0, kDTnull, 1, 0, kDTsclosed_1w, kDTnull, kALclosewdoors_1w}
+#define cutrope_1w {kVCut_1w, kRknife_1w, kDTrnoknife_1w, 0, 1, kDTsnocut_1w, kDTscut_1w, kALcutrope_1w}
+#define dropmask_1w {kVDrop_1w, 0, kDTnull, 0, 0, kDTsworn3_1w, kDTnull, kALdropmask_1w}
+#define droppkin_1w {kVDrop_1w, kRpkin_1w, kDTnocgen_1w, 0, 1, kDTnull, kDTnull, kALpkin_1w}
+#define eatchop_1w {kVEat_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALeatchop2_1w}
+#define getchop_1w {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALgetchop_1w}
+#define getdw_1w {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALcupbdw_1w}
+#define getinboat_1w {kVInto_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetinboat_1w}
+#define getknife_1w {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALcupbpk_1w}
+#define getoilcan_1w {kVTake_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALshedoil_1w}
+#define getoutboat_1w {kVOutof_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetoutboat_1w}
+#define givegold_1w {kVGive_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALgold_1w}
+#define hiderock_1w {kVHide_1w, 0, kDTnull, 0, 1, kDTsrock_1w, kDTnull, kALrock_1w}
+#define kickpkin_1w {kVAttack_1w, 0, kDTnull, 0, 1, kDTnull, kDTokgen_1w, kALpkin_1w}
+#define knock_1w {kVKnock_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_1w, 0}
+#define lockbolt_1w {kVLock_1w, 0, kDTnull, 2, 1, kDTsclosed_1w, kDTsclosebolt_1w, 0}
+#define lockdoor_1w {kVLock_1w, kRkey_1w, kDTnockey_1w, 1, 0, kDTslock_1w, kDTokgen_1w, kALclosedoor1_1w}
+#define lookcupb_1w {kVLook_1w, kRcandle_1w, kDTnoccandle_1w, DONT_CARE, 0, kDTnull, kDTnull, kALlookcupb_1w}
+#define lookshed_1w {kVLook_1w, kRcandle_1w, kDTnoccandle_1w, 0, 0, kDTsnoseeoil_1w, kDTsseeoil_1w, kALshedoil_1w}
+#define movecarp1_1w {kVMove_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w}
+#define movecarp2_1w {kVLift_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w}
+#define movecarp3_1w {kVUnder_1w, 0, kDTnull, 0, 0, kDTnull, kDTsrollrug_1w, kALmovecarp_1w}
+#define offmask_1w {kVOff_1w, 0, kDTnull, 1, 0, kDTsworn2_1w, kDTokgen_1w, kALswapmask_1w}
+#define oilbolt_1w {kVOil_1w, kRoil_1w, kDTrnooil_1w, 0, 1, kDTsoiled_1w, kDTsoilbolt_1w, 0}
+#define omattack_1w {kVAttack_1w, 0, kDTnull, 0, 0, kDTnull, kDTsomattack_1w, 0}
+#define ombreak_1w {kVBreak_1w, 0, kDTnull, 0, 0, kDTnull, kDTsomattack_1w, 0}
+#define omtalk_1w {kVTalk_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALoldman_1w}
+#define openbolt_1w {kVOpen_1w, 0, kDTnull, 1, 2, kDTsstuck_1w, kDTsopenbolt_1w, 0}
+#define opendoor1_1w {kVOpen_1w, kRkey_1w, kDTslocked_1w, 0, 1, kDTsopen_1w, kDTsunlock_1w, kALopendoor1_1w}
+#define opendoor2_1w {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopendoor2_1w}
+#define opendoor3_1w {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopendoor3_1w}
+#define opendoor4_1w {kVOpen_1w, 0, kDTnull, 0, 0, kDTsopen_1w, kDTnull, kALopendoor4_1w}
+#define openpkin_1w {kVOpen_1w, 0, kDTnull, 0, 1, kDTnull, kDTsopenpkin_1w, kALpkin_1w}
+#define opentrap_1w {kVOpen_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALopentrap_1w}
+#define openwdoors_1w {kVOpen_1w, 0, kDTnull, 0, 1, kDTsopen_1w, kDTnull, kALopenwdoors_1w}
+#define plugbung_1w {kVPlug_1w, kRbung_1w, kDTnocgen_1w, 0, 1, kDTnull, kDTsplug_1w, kALplugbung_1w}
+#define pushboat_1w {kVPush_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALpushboat_1w}
+#define pushigor_1w {kVPush_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALigor_1w}
+#define ruboilcan_1w {kVRub_1w, 0, kDTnull, 0, 0, kDTnull, kDTsruboil_1w, 0}
+#define talkdrac_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkdrac_1w}
+#define talkfrank_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkfrank_1w}
+#define talkgwen_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkgwen_1w}
+#define talkhood_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkhood_1w}
+#define talkpeahd_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkpeahd_1w}
+#define talkslime_1w {kVTalk_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALtalkslime_1w}
+#define throwchop_1w {kVThrowit_1w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALthrowchop_1w}
+#define unlkdoor_1w {kVUnlock_1w, kRkey_1w, kDTnockey_1w, 0, 1, kDTsunlocked_1w, kDTsunlock_1w, kALopendoor1_1w}
+#define unlock_1w {kVUnlock_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_1w, 0}
+#define unlockbolt_1w {kVUnlock_1w, 0, kDTnull, 1, 2, kDTsstuck_1w, kDTsopenbolt_1w, 0}
+#define untierope_1w {kVUntie_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsuntie_1w, 0}
+#define useboat_1w {kVMakeUseOf_1w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALuseboat_1w}
+#define usemask_1w {kVMakeUseOf_1w, kRmask_1w, kDTnocgen_1w, DONT_CARE, 0, kDTnull, kDTnull, kALusemask_1w}
+#define wearmask_1w {kVWear_1w, kRmask_1w, kDTnocgen_1w, 0, 1, kDTsworn1_1w, kDTokgen_1w, kALswapmask_1w}
+
+#define emptyCmd {0, 0, kDTnull, 0, 0, kDTnull, kDTnull, 0}
cmd cmdDummy[] = {emptyCmd};
@@ -4618,72 +4388,72 @@ const cmd *cmdList_1w[] = {
shed_1w, slime_1w, trap_1w, ward_1w, whistle_1w
};
-cmd climbdumb_2w = {kVClimb_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALdumb_2w};
-cmd climbrope_2w = {kVClimb_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALclimbrope_2w};
-cmd climbwell_2w = {kVClimb_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALclimbwell_2w};
-cmd closedoor1_2w = {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0};
-cmd closedoor2_2w = {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0};
-cmd closedoor3_2w = {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0};
-cmd closesafe_2w = {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTokgen_2w, 0};
-cmd dialphone_2w = {kVDial_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphone_2w};
-cmd doorpencil_2w = {kVMakeUseOf_2w, kRpencil_2w, kDTnocgen_2w, 0, 1, kDTspencil_2w, kDTnull, kALchkpap1_2w};
-cmd dropdynamite_2w = {kVDrop_2w, kRdyn_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALdropdynamite_2w};
-cmd eatbanana_2w = {kVEat_2w, kRbanana_2w, kDTnocgen_2w, 0, 0, kDTnull, kDTnull, kALeatbanana_2w};
-cmd eatcatnip_2w = {kVEat_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTseatnip_2w, 0};
-cmd eatgarlic_2w = {kVEat_2w, kRgarlic_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALgarlic_2w};
-cmd firegun_2w = {kVFire_2w, kRgun_2w, kDTnogun_2w, 0, 1, kDTsempty_2w, kDTnull, kALgun_2w};
-cmd gard1_2w = {kVTalk_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalkgard_2w};
-cmd gard2_2w = {kVLook_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALlookgard_2w};
-cmd getballoon_2w = {kVTake_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALballoon_2w};
-cmd getbook_2w = {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetbook_2w};
-cmd getdynamite_2w = {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetdynamite_2w};
-cmd getletter_2w = {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsgetlet_2w, 0};
-cmd givebanana_2w = {kVGive_2w, kRbanana_2w, kDTnocgen_2w, 0, 0, kDTnull, kDTnull, kALbanana_2w};
-cmd givebell_2w = {kVGive_2w, kRbell_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALgivebel_2w};
-cmd givecatnip_2w = {kVGive_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTscatnip_2w, 0};
-cmd intodumb_2w = {kVInto_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALdumb_2w};
-cmd knock_2w = {kVKnock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_2w, 0};
-cmd lightdynamite_2w = {kVStrike_2w, kRmatch_2w, kDTnomatch_2w, DONT_CARE, 0, kDTnull, kDTnull, kALlightdynamite_2w};
-cmd lookcubp_2w = {kVLook_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphoto_2w};
-cmd lookgarlic_2w = {kVLook_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindClove_2w, kALgetgarlic_2w};
-cmd lookhole_2w = {kVLook_2w, 0, kDTnull, 0, 0, kDTsDarkHole_2w, kDTnull, kALkeyhole_2w};
-cmd lookkennel_2w = {kVLook_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALlookkennel_2w};
-cmd lookmat_2w = {kVLook_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindMatch_2w, kALgetmatch_2w};
-cmd opencubp_2w = {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphoto_2w};
-cmd opendoor1_2w = {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor1_2w};
-cmd opendoor2_2w = {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor2_2w};
-cmd opendoor3_2w = {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor3_2w};
-cmd opendum_2w = {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsopendum_2w, 0};
-cmd opengarlic_2w = {kVOpen_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindClove_2w, kALgetgarlic_2w};
-cmd openkdoor_2w = {kVOpen_2w, 0, kDTnull, 0, 0, kDTnull, kDTwontopen_2w, 0};
-cmd openlamp_2w = {kVOpen_2w, kRlamp_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTempty_2w, 0};
-cmd openmat_2w = {kVOpen_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindMatch_2w, kALgetmatch_2w};
-cmd openpdoor_2w = {kVOpen_2w, 0, kDTnull, 0, 0, kDTnull, kDTspdoor_2w, 0};
-cmd opensafe_2w = {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALsafe_2w};
-cmd popballoon_2w = {kVBreak_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALballoon_2w};
-cmd pushblue_2w = {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALworkgates_2w};
-cmd pushbutton_2w = {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsWhichColor_2w, 0};
-cmd pushgreen_2w = {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALbugzapper_2w};
-cmd pushpaper_2w = {kVPush_2w, kRpaper_2w, kDTnocgen_2w, 0, 1, kDTsnopaper_2w, kDTspaper_2w, kALpushpaper_2w};
-cmd pushpencil_2w = {kVPush_2w, kRpencil_2w, kDTnocgen_2w, 0, 0, kDTspencil_2w, kDTnull, kALpushpencil_2w};
-cmd pushred_2w = {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALshedlight_2w};
-cmd pushyellow_2w = {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgatelight_2w};
-cmd readalbum_2w = {kVRead_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTtalbum_2w, 0};
-cmd readletter_2w = {kVRead_2w, 0, kDTnull, 3, 3, kDTsnoread_2w, kDTsread_2w, kALreadlet_2w};
-cmd readwill_2w = {kVRead_2w, kRwill_2w, kDTnocgen_2w, 1, 1, kDTnull, kDTnull, kALwill_2w};
-cmd ringbell_2w = {kVRing_2w, kRbell_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALbell_2w};
-cmd rubcatnip_2w = {kVRub_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTnull, kALcatnip_2w};
-cmd rublamp_2w = {kVRub_2w, kRlamp_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTnull, kALlamp_2w};
-cmd serum_2w = {kVDrink_2w, kRserum_2w, kDTnocgen_2w, 0, 1, kDTsnoserum_2w, kDTnull, kALbottle_2w};
-cmd strikematch_2w = {kVStrike_2w, kRmatch_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALstrikematch_2w};
-cmd takepaper_2w = {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakepaper_2w};
-cmd takephone_2w = {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphone_2w};
-cmd talkharry_2w = {kVTalk_2w, 0, kDTnull, 0, 1, kDTsharry_2w, kDTnull, kALharry_2w};
-cmd throwstick_2w = {kVThrowit_2w, kRstick_2w, kDTnocgen_2w, 0, 1, kDTnull, kDTnull, kALthrowstick_2w};
-cmd unlock_2w = {kVUnlock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_2w, 0};
-cmd unlockdum_2w = {kVUnlock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsunlockdum_2w, 0};
-cmd usedynamite_2w = {kVMakeUseOf_2w, kRmatch_2w, kDTnomatch_2w, DONT_CARE, 0, kDTnull, kDTnull, kALlightdynamite_2w};
-cmd userobot_2w = {kVMakeUseOf_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, 0};
+#define climbdumb_2w {kVClimb_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALdumb_2w}
+#define climbrope_2w {kVClimb_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALclimbrope_2w}
+#define climbwell_2w {kVClimb_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALclimbwell_2w}
+#define closedoor1_2w {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0}
+#define closedoor2_2w {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0}
+#define closedoor3_2w {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTnull, 0}
+#define closesafe_2w {kVClose_2w, 0, kDTnull, 1, 0, kDTsclose_2w, kDTokgen_2w, 0}
+#define dialphone_2w {kVDial_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphone_2w}
+#define doorpencil_2w {kVMakeUseOf_2w, kRpencil_2w, kDTnocgen_2w, 0, 1, kDTspencil_2w, kDTnull, kALchkpap1_2w}
+#define dropdynamite_2w {kVDrop_2w, kRdyn_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALdropdynamite_2w}
+#define eatbanana_2w {kVEat_2w, kRbanana_2w, kDTnocgen_2w, 0, 0, kDTnull, kDTnull, kALeatbanana_2w}
+#define eatcatnip_2w {kVEat_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTseatnip_2w, 0}
+#define eatgarlic_2w {kVEat_2w, kRgarlic_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALgarlic_2w}
+#define firegun_2w {kVFire_2w, kRgun_2w, kDTnogun_2w, 0, 1, kDTsempty_2w, kDTnull, kALgun_2w}
+#define gard1_2w {kVTalk_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalkgard_2w}
+#define gard2_2w {kVLook_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALlookgard_2w}
+#define getballoon_2w {kVTake_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALballoon_2w}
+#define getbook_2w {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetbook_2w}
+#define getdynamite_2w {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgetdynamite_2w}
+#define getletter_2w {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsgetlet_2w, 0}
+#define givebanana_2w {kVGive_2w, kRbanana_2w, kDTnocgen_2w, 0, 0, kDTnull, kDTnull, kALbanana_2w}
+#define givebell_2w {kVGive_2w, kRbell_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALgivebel_2w}
+#define givecatnip_2w {kVGive_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTscatnip_2w, 0}
+#define intodumb_2w {kVInto_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALdumb_2w}
+#define knock_2w {kVKnock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_2w, 0}
+#define lightdynamite_2w {kVStrike_2w, kRmatch_2w, kDTnomatch_2w, DONT_CARE, 0, kDTnull, kDTnull, kALlightdynamite_2w}
+#define lookcubp_2w {kVLook_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphoto_2w}
+#define lookgarlic_2w {kVLook_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindClove_2w, kALgetgarlic_2w}
+#define lookhole_2w {kVLook_2w, 0, kDTnull, 0, 0, kDTsDarkHole_2w, kDTnull, kALkeyhole_2w}
+#define lookkennel_2w {kVLook_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALlookkennel_2w}
+#define lookmat_2w {kVLook_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindMatch_2w, kALgetmatch_2w}
+#define opencubp_2w {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphoto_2w}
+#define opendoor1_2w {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor1_2w}
+#define opendoor2_2w {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor2_2w}
+#define opendoor3_2w {kVOpen_2w, 0, kDTnull, 0, 1, kDTsopen1_2w, kDTnull, kALopendoor3_2w}
+#define opendum_2w {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsopendum_2w, 0}
+#define opengarlic_2w {kVOpen_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindClove_2w, kALgetgarlic_2w}
+#define openkdoor_2w {kVOpen_2w, 0, kDTnull, 0, 0, kDTnull, kDTwontopen_2w, 0}
+#define openlamp_2w {kVOpen_2w, kRlamp_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTempty_2w, 0}
+#define openmat_2w {kVOpen_2w, 0, kDTnull, 0, 1, kDTempty_2w, kDTsFindMatch_2w, kALgetmatch_2w}
+#define openpdoor_2w {kVOpen_2w, 0, kDTnull, 0, 0, kDTnull, kDTspdoor_2w, 0}
+#define opensafe_2w {kVOpen_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALsafe_2w}
+#define popballoon_2w {kVBreak_2w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALballoon_2w}
+#define pushblue_2w {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALworkgates_2w}
+#define pushbutton_2w {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsWhichColor_2w, 0}
+#define pushgreen_2w {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALbugzapper_2w}
+#define pushpaper_2w {kVPush_2w, kRpaper_2w, kDTnocgen_2w, 0, 1, kDTsnopaper_2w, kDTspaper_2w, kALpushpaper_2w}
+#define pushpencil_2w {kVPush_2w, kRpencil_2w, kDTnocgen_2w, 0, 0, kDTspencil_2w, kDTnull, kALpushpencil_2w}
+#define pushred_2w {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALshedlight_2w}
+#define pushyellow_2w {kVPush_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgatelight_2w}
+#define readalbum_2w {kVRead_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTtalbum_2w, 0}
+#define readletter_2w {kVRead_2w, 0, kDTnull, 3, 3, kDTsnoread_2w, kDTsread_2w, kALreadlet_2w}
+#define readwill_2w {kVRead_2w, kRwill_2w, kDTnocgen_2w, 1, 1, kDTnull, kDTnull, kALwill_2w}
+#define ringbell_2w {kVRing_2w, kRbell_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALbell_2w}
+#define rubcatnip_2w {kVRub_2w, kRcatnip_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTnull, kALcatnip_2w}
+#define rublamp_2w {kVRub_2w, kRlamp_2w, kDTnocgen_2w, 0, 0, kDTnopurps_2w, kDTnull, kALlamp_2w}
+#define serum_2w {kVDrink_2w, kRserum_2w, kDTnocgen_2w, 0, 1, kDTsnoserum_2w, kDTnull, kALbottle_2w}
+#define strikematch_2w {kVStrike_2w, kRmatch_2w, kDTnocgen_2w, DONT_CARE, 0, kDTnull, kDTnull, kALstrikematch_2w}
+#define takepaper_2w {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakepaper_2w}
+#define takephone_2w {kVTake_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALphone_2w}
+#define talkharry_2w {kVTalk_2w, 0, kDTnull, 0, 1, kDTsharry_2w, kDTnull, kALharry_2w}
+#define throwstick_2w {kVThrowit_2w, kRstick_2w, kDTnocgen_2w, 0, 1, kDTnull, kDTnull, kALthrowstick_2w}
+#define unlock_2w {kVUnlock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_2w, 0}
+#define unlockdum_2w {kVUnlock_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsunlockdum_2w, 0}
+#define usedynamite_2w {kVMakeUseOf_2w, kRmatch_2w, kDTnomatch_2w, DONT_CARE, 0, kDTnull, kDTnull, kALlightdynamite_2w}
+#define userobot_2w {kVMakeUseOf_2w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, 0}
cmd album_2w[] = {readalbum_2w, emptyCmd};
cmd balloon_2w[] = {popballoon_2w, getballoon_2w, emptyCmd};
@@ -4739,59 +4509,59 @@ const cmd *cmdList_2w[] = {
will_2w, yellow_2w
};
-cmd bell_3w = {kVRing_3w, kRbell_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTokbell_3w, 0};
-cmd blow_3w = {kVBlow_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTnull, kALdart_3w};
-cmd blowdoc_3w = {kVShoot_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTsblowdoc_3w, 0};
-cmd book_3w = {kVRead_3w, kRbook_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTnull, kALreadbook_3w};
-cmd cage1_3w = {kVOpen_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALopencage_3w};
-cmd cage2_3w = {kVClose_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, 0};
-cmd cage3_3w = {kVTake_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakecage_3w};
-cmd cageuse_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALusecage_3w};
-cmd candle1_3w = {kVStrike_3w, 0, kDTnull, 0, 1, kDTslit_3w, kDTokgen_3w, 0};
-cmd candle2_3w = {kVDouse_3w, 0, kDTnull, 1, 0, kDTsunlit_3w, kDTokgen_3w, 0};
-cmd cdoor1_3w = {kVOpen_3w, 0, kDTnull, 0, 0, kDTsopen1_3w, kDTnull, kALopendoor_3w};
-cmd cdoor2_3w = {kVClose_3w, 0, kDTnull, 1, 0, kDTsclose_3w, kDTokgen_3w, kALclosedoor_3w};
-cmd cdrinkpool_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTspool_3w, 0};
-cmd cdrinkstream_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsstream_3w, 0};
-cmd cexit1_3w = {kVOutof_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALexit_3w};
-cmd cexit2_3w = {kVClimb_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALexit_3w};
-cmd cflask1_3w = {kVFill_3w, 0, kDTnull, 0, 1, kDTsfull_3w, kDTnull, kALfill_3w};
-cmd cflask2_3w = {kVPut_3w, 0, kDTnull, 0, 1, kDTsfull_3w, kDTnull, kALfill_3w};
-cmd cflask3_3w = {kVEmpty_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALempty2_3w};
-cmd cflask4_3w = {kVDrink_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdrink_3w};
-cmd cflask5_3w = {kVGive_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALflask_3w};
-cmd cheese1_3w = {kVEat_3w, kRcheese_3w, kDTnocgen_3w, 0, 0, kDTnull, kDTnull, kALeatcheese_3w};
-cmd cheese2_3w = {kVDrop_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdropcheese_3w};
-cmd cheese3_3w = {kVPut_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdropcheese_3w};
-cmd cheese4_3w = {kVTake_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakecheese_3w};
-cmd cmake1_3w = {kVMake_3w, 0, kDTnull, 0, 0, kDTsmade_3w, kDTnull, kALmakeclay_3w};
-cmd cmake2_3w = {kVStick_3w, kRpins_3w, kDTnopins_3w, DONT_CARE, 0, kDTnull, kDTnull, kALstick_3w};
-cmd cplane1_3w = {kVClimb_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w};
-cmd cplane2_3w = {kVInto_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w};
-cmd cplane3_3w = {kVSearch_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w};
-cmd crystal_3w = {kVRub_3w, kRcrystal_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTnull, kALcrystal_3w};
-cmd csteps1_3w = {kVMakeUseOf_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALsteps_3w};
-cmd cstick1_3w = {kVStick_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALstick_3w};
-cmd cswing1_3w = {kVSwing_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswinger_3w, kALswing_3w};
-cmd ctalknat_3w = {kVTalk_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalknat_3w};
-cmd cube1_3w = {kVGive_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgiveb_3w};
-cmd cvine1_3w = {kVMakeUseOf_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALvine_3w};
-cmd cvine2_3w = {kVUntie_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALuntie_vine_3w};
-cmd cvine3_3w = {kVTie_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALvine_3w};
-cmd cwaterfall_3w = {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALlookwfall_3w};
-cmd cwaterpool_3w = {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswater_3w, 0};
-cmd cwaterstream_3w = {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswater_3w, 0};
-cmd dart_3w = {kVShoot_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTnull, kALdart_3w};
-cmd elephant_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTuelephant_3w, 0};
-cmd ghost1_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTughost_3w, 0};
-cmd ghost2_3w = {kVExorcise_3w, kRexor_3w, kDTnocex_3w, DONT_CARE, 0, kDTnull, kDTnull, kALexorcise_3w};
-cmd knock_3w = {kVKnock_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_3w, 0};
-cmd lookrush_3w = {kVBehind_3w, 0, kDTnull, 0, 1, kDTsfoundb_3w, kDTnull, kALfindbook_3w};
-cmd readit_3w = {kVRead_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsread_3w, 0};
-cmd rock1_3w = {kVBehind_3w, 0, kDTnull, 0, 1, kDTsfoundc_3w, kDTsfindc_3w, kALfindcrystal_3w};
-cmd swingc_3w = {kVSwing_3w, 0, kDTnull, 0, 1, kDTsnoswing_3w, kDTswingcave_3w, 0};
-cmd unlock_3w = {kVUnlock_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_3w, 0};
-cmd usedoc_3w = {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalkdoc_3w};
+#define bell_3w {kVRing_3w, kRbell_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTokbell_3w, 0}
+#define blow_3w {kVBlow_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTnull, kALdart_3w}
+#define blowdoc_3w {kVShoot_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTsblowdoc_3w, 0}
+#define book_3w {kVRead_3w, kRbook_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTnull, kALreadbook_3w}
+#define cage1_3w {kVOpen_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALopencage_3w}
+#define cage2_3w {kVClose_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, 0}
+#define cage3_3w {kVTake_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakecage_3w}
+#define cageuse_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALusecage_3w}
+#define candle1_3w {kVStrike_3w, 0, kDTnull, 0, 1, kDTslit_3w, kDTokgen_3w, 0}
+#define candle2_3w {kVDouse_3w, 0, kDTnull, 1, 0, kDTsunlit_3w, kDTokgen_3w, 0}
+#define cdoor1_3w {kVOpen_3w, 0, kDTnull, 0, 0, kDTsopen1_3w, kDTnull, kALopendoor_3w}
+#define cdoor2_3w {kVClose_3w, 0, kDTnull, 1, 0, kDTsclose_3w, kDTokgen_3w, kALclosedoor_3w}
+#define cdrinkpool_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTspool_3w, 0}
+#define cdrinkstream_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsstream_3w, 0}
+#define cexit1_3w {kVOutof_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALexit_3w}
+#define cexit2_3w {kVClimb_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALexit_3w}
+#define cflask1_3w {kVFill_3w, 0, kDTnull, 0, 1, kDTsfull_3w, kDTnull, kALfill_3w}
+#define cflask2_3w {kVPut_3w, 0, kDTnull, 0, 1, kDTsfull_3w, kDTnull, kALfill_3w}
+#define cflask3_3w {kVEmpty_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALempty2_3w}
+#define cflask4_3w {kVDrink_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdrink_3w}
+#define cflask5_3w {kVGive_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALflask_3w}
+#define cheese1_3w {kVEat_3w, kRcheese_3w, kDTnocgen_3w, 0, 0, kDTnull, kDTnull, kALeatcheese_3w}
+#define cheese2_3w {kVDrop_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdropcheese_3w}
+#define cheese3_3w {kVPut_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALdropcheese_3w}
+#define cheese4_3w {kVTake_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtakecheese_3w}
+#define cmake1_3w {kVMake_3w, 0, kDTnull, 0, 0, kDTsmade_3w, kDTnull, kALmakeclay_3w}
+#define cmake2_3w {kVStick_3w, kRpins_3w, kDTnopins_3w, DONT_CARE, 0, kDTnull, kDTnull, kALstick_3w}
+#define cplane1_3w {kVClimb_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w}
+#define cplane2_3w {kVInto_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w}
+#define cplane3_3w {kVSearch_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTokgen_3w, kALplane_3w}
+#define crystal_3w {kVRub_3w, kRcrystal_3w, kDTnocgen_3w, DONT_CARE, 0, kDTnull, kDTnull, kALcrystal_3w}
+#define csteps1_3w {kVMakeUseOf_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALsteps_3w}
+#define cstick1_3w {kVStick_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALstick_3w}
+#define cswing1_3w {kVSwing_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswinger_3w, kALswing_3w}
+#define ctalknat_3w {kVTalk_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalknat_3w}
+#define cube1_3w {kVGive_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALgiveb_3w}
+#define cvine1_3w {kVMakeUseOf_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALvine_3w}
+#define cvine2_3w {kVUntie_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALuntie_vine_3w}
+#define cvine3_3w {kVTie_3w, 0, kDTnull, 0, 0, kDTnull, kDTnull, kALvine_3w}
+#define cwaterfall_3w {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALlookwfall_3w}
+#define cwaterpool_3w {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswater_3w, 0}
+#define cwaterstream_3w {kVLook_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTswater_3w, 0}
+#define dart_3w {kVShoot_3w, kRpipe_3w, kDTnogun_3w, DONT_CARE, 0, kDTnull, kDTnull, kALdart_3w}
+#define elephant_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTuelephant_3w, 0}
+#define ghost1_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTughost_3w, 0}
+#define ghost2_3w {kVExorcise_3w, kRexor_3w, kDTnocex_3w, DONT_CARE, 0, kDTnull, kDTnull, kALexorcise_3w}
+#define knock_3w {kVKnock_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsNobody_3w, 0}
+#define lookrush_3w {kVBehind_3w, 0, kDTnull, 0, 1, kDTsfoundb_3w, kDTnull, kALfindbook_3w}
+#define readit_3w {kVRead_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsread_3w, 0}
+#define rock1_3w {kVBehind_3w, 0, kDTnull, 0, 1, kDTsfoundc_3w, kDTsfindc_3w, kALfindcrystal_3w}
+#define swingc_3w {kVSwing_3w, 0, kDTnull, 0, 1, kDTsnoswing_3w, kDTswingcave_3w, 0}
+#define unlock_3w {kVUnlock_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTsUnlocked_3w, 0}
+#define usedoc_3w {kVMakeUseOf_3w, 0, kDTnull, DONT_CARE, 0, kDTnull, kDTnull, kALtalkdoc_3w}
cmd cbell_3w[] = {bell_3w, emptyCmd};
cmd cbook_3w[] = {book_3w, emptyCmd};
@@ -4831,58 +4601,58 @@ const cmd *cmdList_3w[] = {
cvine_3w, cwfall_3w, cwpool_3w, cwstream_3w
};
-cmd blowdw_1d = {kVBlow_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALblowdw_1d};
-cmd breakpkin_1d = {kVBreak_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d};
-cmd brkrope_1d = {kVBreak_1d, 0, 0, DONT_CARE, 0, 0, kDTsrbreak_1d, 0};
-cmd closebolt_1d = {kVClose_1d, 0, 0, 2, 1, kDTsclose_1d, kDTokgen_1d, 0};
-cmd closedoor1_1d = {kVClose_1d, 0, 0, 2, 0, kDTsclose_1d, 0, kALclosedoor1_1d};
-cmd closedoor2_1d = {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, 0};
-cmd closedoor3_1d = {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, 0};
-cmd closedoor4_1d = {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, kALclosedoor4_1d};
-cmd closetrap_1d = {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, kDTokgen_1d, kALclosetrap_1d};
-cmd closewdoors_1d = {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, kALclosewdoors_1d};
-cmd cutrope_1d = {kVCut_1d, kRknife_1d, kDTrnoknife_1d, 0, 1, kDTsnocut_1d, kDTscut_1d, kALcutrope_1d};
-cmd dropmask_1d = {kVDrop_1d, 0, 0, 0, 0, kDTsworn3_1d, 0, kALdropmask_1d};
-cmd droppkin_1d = {kVDrop_1d, kRpkin_1d, kDTnocgen_1d, 0, 1, 0, 0, kALpkin_1d};
-cmd eatchop_1d = {kVEat_1d, 0, 0, 0, 0, 0, 0, kALeatchop_1d};
-cmd getdw_1d = {kVTake_1d, 0, 0, 0, 0, 0, 0, kALcupbdw_1d};
-cmd getinboat_1d = {kVInto_1d, 0, 0, DONT_CARE, 0, 0, 0, kALgetinboat_1d};
-cmd getknife_1d = {kVTake_1d, 0, 0, 0, 0, 0, 0, kALcupbpk_1d};
-cmd getoilcan_1d = {kVTake_1d, 0, 0, 0, 0, 0, 0, kALshedoil_1d};
-cmd getoutboat_1d = {kVOutof_1d, 0, 0, DONT_CARE, 0, 0, 0, kALgetoutboat_1d};
-cmd givegold_1d = {kVGive_1d, 0, 0, 0, 0, 0, 0, kALgold_1d};
-cmd kickpkin_1d = {kVAttack_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d};
-cmd knock_1d = {kVKnock_1d, 0, 0, DONT_CARE, 0, 0, kDTsknock_1d, 0};
-cmd lockbolt_1d = {kVLock_1d, 0, 0, 2, 1, kDTsclose_1d, kDTokgen_1d, 0};
-cmd lockdoor_1d = {kVLock_1d, kRkey_1d, kDTnockey_1d, 2, 0, kDTslock_1d, kDTokgen_1d, 0};
-cmd lookcupb_1d = {kVLook_1d, kRcandle_1d, kDTnoccandle_1d, DONT_CARE, 0, 0, 0, kALlookcupb_1d};
-cmd lookshed_1d = {kVLook_1d, kRcandle_1d, kDTnoccandle_1d, 0, 0, kDTsnosee_1d, kDTsseeoil_1d, 0};
-cmd movecarp1_1d = {kVMove_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d};
-cmd movecarp2_1d = {kVLift_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d};
-cmd movecarp3_1d = {kVUnder_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d};
-cmd offmask_1d = {kVOff_1d, 0, 0, 1, 0, kDTsworn2_1d, kDTokgen_1d, kALswapmask_1d};
-cmd oilbolt_1d = {kVOil_1d, kRoil_1d, kDTrnooil_1d, 0, 1, kDTsoiled_1d, kDTokgen_1d, 0};
-cmd omattack_1d = {kVAttack_1d, 0, 0, 0, 0, 0, kDTsomattack_1d, 0};
-cmd ombreak_1d = {kVBreak_1d, 0, 0, 0, 0, 0, kDTsomattack_1d, 0};
-cmd omtalk_1d = {kVTalk_1d, 0, 0, DONT_CARE, 0, 0, 0, kALoldman_1d};
-cmd openbolt_1d = {kVOpen_1d, 0, 0, 1, 2, kDTsstuck_1d, kDTokgen_1d, 0};
-cmd opendoor1_1d = {kVOpen_1d, 0, 0, 1, 2, kDTsopen_1d, 0, kALopendoor1_1d};
-cmd opendoor2_1d = {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopendoor2_1d};
-cmd opendoor3_1d = {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopendoor3_1d};
-cmd opendoor4_1d = {kVOpen_1d, 0, 0, 0, 0, kDTsopen2_1d, 0, kALopendoor4_1d};
-cmd openpkin_1d = {kVOpen_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d};
-cmd opentrap_1d = {kVOpen_1d, 0, 0, 0, 0, 0, 0, kALopentrap_1d};
-cmd openwdoors_1d = {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopenwdoors_1d};
-cmd plugbung_1d = {kVPlug_1d, kRbung_1d, kDTnocgen_1d, 0, 1, 0, kDTokgen_1d, kALplugbung_1d};
-cmd pushboat_1d = {kVPush_1d, 0, 0, DONT_CARE, 0, 0, 0, kALpushboat_1d};
-cmd pushigor_1d = {kVPush_1d, 0, 0, 0, 0, 0, 0, kALigor_1d};
-cmd ruboilcan_1d = {kVRub_1d, 0, 0, 0, 0, 0, kDTsruboil_1d, 0};
-cmd throwchop_1d = {kVThrow_1d, 0, 0, 0, 0, 0, 0, kALthrowchop_1d};
-cmd unlkdoor_1d = {kVUnlock_1d, kRkey_1d, kDTnockey_1d, 0, 1, kDTsunlock_1d, kDTokgen_1d, 0};
-cmd unlock_1d = {kVUnlock_1d, 0, 0, DONT_CARE, 0, 0, kDTsunlock_1d, 0};
-cmd unlockbolt_1d = {kVUnlock_1d, 0, 0, 1, 2, kDTsstuck_1d, kDTokgen_1d, 0};
-cmd untierope_1d = {kVUntie_1d, 0, 0, DONT_CARE, 0, 0, kDTsuntie_1d, 0};
-cmd wearmask_1d = {kVWear_1d, kRmask_1d, kDTnocgen_1d, 0, 1, kDTsworn1_1d, kDTokgen_1d, kALswapmask_1d};
+#define blowdw_1d {kVBlow_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALblowdw_1d}
+#define breakpkin_1d {kVBreak_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d}
+#define brkrope_1d {kVBreak_1d, 0, 0, DONT_CARE, 0, 0, kDTsrbreak_1d, 0}
+#define closebolt_1d {kVClose_1d, 0, 0, 2, 1, kDTsclose_1d, kDTokgen_1d, 0}
+#define closedoor1_1d {kVClose_1d, 0, 0, 2, 0, kDTsclose_1d, 0, kALclosedoor1_1d}
+#define closedoor2_1d {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, 0}
+#define closedoor3_1d {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, 0}
+#define closedoor4_1d {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, kALclosedoor4_1d}
+#define closetrap_1d {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, kDTokgen_1d, kALclosetrap_1d}
+#define closewdoors_1d {kVClose_1d, 0, 0, 1, 0, kDTsclose_1d, 0, kALclosewdoors_1d}
+#define cutrope_1d {kVCut_1d, kRknife_1d, kDTrnoknife_1d, 0, 1, kDTsnocut_1d, kDTscut_1d, kALcutrope_1d}
+#define dropmask_1d {kVDrop_1d, 0, 0, 0, 0, kDTsworn3_1d, 0, kALdropmask_1d}
+#define droppkin_1d {kVDrop_1d, kRpkin_1d, kDTnocgen_1d, 0, 1, 0, 0, kALpkin_1d}
+#define eatchop_1d {kVEat_1d, 0, 0, 0, 0, 0, 0, kALeatchop_1d}
+#define getdw_1d {kVTake_1d, 0, 0, 0, 0, 0, 0, kALcupbdw_1d}
+#define getinboat_1d {kVInto_1d, 0, 0, DONT_CARE, 0, 0, 0, kALgetinboat_1d}
+#define getknife_1d {kVTake_1d, 0, 0, 0, 0, 0, 0, kALcupbpk_1d}
+#define getoilcan_1d {kVTake_1d, 0, 0, 0, 0, 0, 0, kALshedoil_1d}
+#define getoutboat_1d {kVOutof_1d, 0, 0, DONT_CARE, 0, 0, 0, kALgetoutboat_1d}
+#define givegold_1d {kVGive_1d, 0, 0, 0, 0, 0, 0, kALgold_1d}
+#define kickpkin_1d {kVAttack_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d}
+#define knock_1d {kVKnock_1d, 0, 0, DONT_CARE, 0, 0, kDTsknock_1d, 0}
+#define lockbolt_1d {kVLock_1d, 0, 0, 2, 1, kDTsclose_1d, kDTokgen_1d, 0}
+#define lockdoor_1d {kVLock_1d, kRkey_1d, kDTnockey_1d, 2, 0, kDTslock_1d, kDTokgen_1d, 0}
+#define lookcupb_1d {kVLook_1d, kRcandle_1d, kDTnoccandle_1d, DONT_CARE, 0, 0, 0, kALlookcupb_1d}
+#define lookshed_1d {kVLook_1d, kRcandle_1d, kDTnoccandle_1d, 0, 0, kDTsnosee_1d, kDTsseeoil_1d, 0}
+#define movecarp1_1d {kVMove_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d}
+#define movecarp2_1d {kVLift_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d}
+#define movecarp3_1d {kVUnder_1d, 0, 0, 0, 0, 0, kDTokgen_1d, kALmovecarp_1d}
+#define offmask_1d {kVOff_1d, 0, 0, 1, 0, kDTsworn2_1d, kDTokgen_1d, kALswapmask_1d}
+#define oilbolt_1d {kVOil_1d, kRoil_1d, kDTrnooil_1d, 0, 1, kDTsoiled_1d, kDTokgen_1d, 0}
+#define omattack_1d {kVAttack_1d, 0, 0, 0, 0, 0, kDTsomattack_1d, 0}
+#define ombreak_1d {kVBreak_1d, 0, 0, 0, 0, 0, kDTsomattack_1d, 0}
+#define omtalk_1d {kVTalk_1d, 0, 0, DONT_CARE, 0, 0, 0, kALoldman_1d}
+#define openbolt_1d {kVOpen_1d, 0, 0, 1, 2, kDTsstuck_1d, kDTokgen_1d, 0}
+#define opendoor1_1d {kVOpen_1d, 0, 0, 1, 2, kDTsopen_1d, 0, kALopendoor1_1d}
+#define opendoor2_1d {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopendoor2_1d}
+#define opendoor3_1d {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopendoor3_1d}
+#define opendoor4_1d {kVOpen_1d, 0, 0, 0, 0, kDTsopen2_1d, 0, kALopendoor4_1d}
+#define openpkin_1d {kVOpen_1d, 0, 0, 0, 1, 0, kDTokgen_1d, kALpkin_1d}
+#define opentrap_1d {kVOpen_1d, 0, 0, 0, 0, 0, 0, kALopentrap_1d}
+#define openwdoors_1d {kVOpen_1d, 0, 0, 0, 1, kDTsopen2_1d, 0, kALopenwdoors_1d}
+#define plugbung_1d {kVPlug_1d, kRbung_1d, kDTnocgen_1d, 0, 1, 0, kDTokgen_1d, kALplugbung_1d}
+#define pushboat_1d {kVPush_1d, 0, 0, DONT_CARE, 0, 0, 0, kALpushboat_1d}
+#define pushigor_1d {kVPush_1d, 0, 0, 0, 0, 0, 0, kALigor_1d}
+#define ruboilcan_1d {kVRub_1d, 0, 0, 0, 0, 0, kDTsruboil_1d, 0}
+#define throwchop_1d {kVThrow_1d, 0, 0, 0, 0, 0, 0, kALthrowchop_1d}
+#define unlkdoor_1d {kVUnlock_1d, kRkey_1d, kDTnockey_1d, 0, 1, kDTsunlock_1d, kDTokgen_1d, 0}
+#define unlock_1d {kVUnlock_1d, 0, 0, DONT_CARE, 0, 0, kDTsunlock_1d, 0}
+#define unlockbolt_1d {kVUnlock_1d, 0, 0, 1, 2, kDTsstuck_1d, kDTokgen_1d, 0}
+#define untierope_1d {kVUntie_1d, 0, 0, DONT_CARE, 0, 0, kDTsuntie_1d, 0}
+#define wearmask_1d {kVWear_1d, kRmask_1d, kDTnocgen_1d, 0, 1, kDTsworn1_1d, kDTokgen_1d, kALswapmask_1d}
cmd boat_1d[] = {getinboat_1d, getoutboat_1d, pushboat_1d, emptyCmd};
cmd bolt_1d[] = {oilbolt_1d, openbolt_1d, unlockbolt_1d, closebolt_1d, lockbolt_1d, emptyCmd};
@@ -4916,62 +4686,62 @@ const cmd *cmdList_1d[] = {
ward_1d, whistle_1d
};
-cmd climbdumb_2d = {kVClimb_2d, 0, 0, 0, 0, 0, 0, kALdumb_2d};
-cmd climbrope_2d = {kVClimb_2d, 0, 0, DONT_CARE, 0, 0, 0, kALclimbrope_2d};
-cmd climbwell_2d = {kVClimb_2d, 0, 0, DONT_CARE, 0, 0, 0, kALclimbwell_2d};
-cmd closedoor1_2d = {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0};
-cmd closedoor2_2d = {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0};
-cmd closedoor3_2d = {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0};
-cmd closesafe_2d = {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, kDTokgen_2d, 0};
-cmd dialphone_2d = {kVDial_2d, 0, 0, 1, 1, kDTsdialed_2d, 0, kALphone_2d};
-cmd dropdynamite_2d = {kVDrop_2d, kRdyn_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALdropdynamite_2d};
-cmd eatbanana_2d = {kVEat_2d, kRbanana_2d, kDTnocgen_2d, 0, 0, 0, 0, kALeatbanana_2d};
-cmd eatgarlic_2d = {kVEat_2d, kRgarlic_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALgarlic_2d};
-cmd firegun_2d = {kVFire_2d, kRgun_2d, kDTnogun_2d, 0, 1, kDTsempty_2d, 0, kALgun_2d};
-cmd getballoon_2d = {kVTake_2d, 0, 0, 0, 0, 0, 0, kALballoon_2d};
-cmd getbook_2d = {kVTake_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgetbook_2d};
-cmd getdynamite_2d = {kVTake_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgetdynamite_2d};
-cmd getletter_2d = {kVTake_2d, 0, 0, DONT_CARE, 0, kDTsgetlet_2d, 0, 0};
-cmd givebanana_2d = {kVGive_2d, kRbanana_2d, kDTnocgen_2d, 0, 0, 0, 0, kALbanana_2d};
-cmd givebell_2d = {kVGive_2d, kRbell_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALgivebel_2d};
-cmd givecatnip_2d = {kVGive_2d, kRcatnip_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, kDTscatnip_2d, 0};
-cmd intodumb_2d = {kVInto_2d, 0, 0, 0, 0, 0, 0, kALdumb_2d};
-cmd knock_2d = {kVKnock_2d, 0, 0, DONT_CARE, 0, 0, kDTtnoknock_2d, 0};
-cmd lightdynamite_2d = {kVStrike_2d, kRmatch_2d, kDTnomatch_2d, DONT_CARE, 0, 0, 0, kALlightdynamite_2d};
-cmd lookcubp_2d = {kVLook_2d, 0, 0, DONT_CARE, 0, 0, 0, kALphoto_2d};
-cmd lookgarlic_2d = {kVLook_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindclove_2d, kALgetgarlic_2d};
-cmd lookhole_2d = {kVLook_2d, 0, 0, 0, 0, kDTdarkhole_2d, 0, kALkeyhole_2d};
-cmd lookkennel_2d = {kVLook_2d, 0, 0, 0, 0, 0, 0, kALlookkennel_2d};
-cmd lookmat_2d = {kVLook_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindmatch_2d, kALgetmatch_2d};
-cmd lookmatch_2d = {kVLook_2d, 0, 0, DONT_CARE, 0, 0, 0, kALlookmatch_2d};
-cmd opencubp_2d = {kVOpen_2d, 0, 0, DONT_CARE, 0, 0, 0, kALphoto_2d};
-cmd opendoor1_2d = {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor1_2d};
-cmd opendoor2_2d = {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor2_2d};
-cmd opendoor3_2d = {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor3_2d};
-cmd opengarlic_2d = {kVOpen_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindclove_2d, kALgetgarlic_2d};
-cmd openkdoor_2d = {kVOpen_2d, 0, 0, 0, 0, 0, kDTwontopen_2d, 0};
-cmd openlamp_2d = {kVOpen_2d, kRlamp_2d, kDTnocgen_2d, DONT_CARE, 0, 0, kDTempty_2d, 0};
-cmd openmat_2d = {kVOpen_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindmatch_2d, kALgetmatch_2d};
-cmd openpdoor_2d = {kVOpen_2d, 0, 0, 0, 0, 0, kDTspdoor_2d, 0};
-cmd opensafe_2d = {kVOpen_2d, 0, 0, DONT_CARE, 0, 0, 0, kALsafe_2d};
-cmd popballoon_2d = {kVBreak_2d, 0, 0, 0, 0, 0, 0, kALballoon_2d};
-cmd pushblue_2d = {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALworkgates_2d};
-cmd pushbutton_2d = {kVPush_2d, 0, 0, DONT_CARE, 0, 0, kDTtnopushbutton_2d, 0};
-cmd pushgreen_2d = {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALbugzapper_2d};
-cmd pushpaper_2d = {kVPush_2d, kRpaper_2d, kDTnocgen_2d, 0, 1, 0, kDTokgen_2d, kALpushpaper_2d};
-cmd pushpencil_2d = {kVPush_2d, kRpencil_2d, kDTnocgen_2d, 0, 0, 0, 0, kALpushpencil_2d};
-cmd pushred_2d = {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALshedlight_2d};
-cmd pushyellow_2d = {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgatelight_2d};
-cmd readletter_2d = {kVRead_2d, 0, 0, 3, 3, kDTsnoread_2d, kDTsread_2d, kALreadlet_2d};
-cmd readwill_2d = {kVRead_2d, kRwill_2d, kDTnocgen_2d, 1, 1, 0, 0, kALwill_2d};
-cmd ringbell_2d = {kVRing_2d, kRbell_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALbell_2d};
-cmd rubcatnip_2d = {kVRub_2d, kRcatnip_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, 0, kALcatnip_2d};
-cmd rublamp_2d = {kVRub_2d, kRlamp_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, 0, kALlamp_2d};
-cmd serum_2d = {kVDrink_2d, kRserum_2d, kDTnocgen_2d, 0, 1, kDTsnosee_2d, 0, kALbottle_2d};
-cmd strikematch_2d = {kVStrike_2d, kRmatch_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALstrikematch_2d};
-cmd talkharry_2d = {kVTalk_2d, 0, 0, 0, 1, kDTsharry_2d, 0, kALharry_2d};
-cmd throwstick_2d = {kVThrow_2d, kRstick_2d, kDTnocgen_2d, 0, 1, 0, 0, kALthrowstick_2d};
-cmd unlock_2d = {kVUnlock_2d, 0, 0, DONT_CARE, 0, 0, kDTtnounlock_2d, 0};
+#define climbdumb_2d {kVClimb_2d, 0, 0, 0, 0, 0, 0, kALdumb_2d}
+#define climbrope_2d {kVClimb_2d, 0, 0, DONT_CARE, 0, 0, 0, kALclimbrope_2d}
+#define climbwell_2d {kVClimb_2d, 0, 0, DONT_CARE, 0, 0, 0, kALclimbwell_2d}
+#define closedoor1_2d {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0}
+#define closedoor2_2d {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0}
+#define closedoor3_2d {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, 0, 0}
+#define closesafe_2d {kVClose_2d, 0, 0, 1, 0, kDTsclose_2d, kDTokgen_2d, 0}
+#define dialphone_2d {kVDial_2d, 0, 0, 1, 1, kDTsdialed_2d, 0, kALphone_2d}
+#define dropdynamite_2d {kVDrop_2d, kRdyn_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALdropdynamite_2d}
+#define eatbanana_2d {kVEat_2d, kRbanana_2d, kDTnocgen_2d, 0, 0, 0, 0, kALeatbanana_2d}
+#define eatgarlic_2d {kVEat_2d, kRgarlic_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALgarlic_2d}
+#define firegun_2d {kVFire_2d, kRgun_2d, kDTnogun_2d, 0, 1, kDTsempty_2d, 0, kALgun_2d}
+#define getballoon_2d {kVTake_2d, 0, 0, 0, 0, 0, 0, kALballoon_2d}
+#define getbook_2d {kVTake_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgetbook_2d}
+#define getdynamite_2d {kVTake_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgetdynamite_2d}
+#define getletter_2d {kVTake_2d, 0, 0, DONT_CARE, 0, kDTsgetlet_2d, 0, 0}
+#define givebanana_2d {kVGive_2d, kRbanana_2d, kDTnocgen_2d, 0, 0, 0, 0, kALbanana_2d}
+#define givebell_2d {kVGive_2d, kRbell_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALgivebel_2d}
+#define givecatnip_2d {kVGive_2d, kRcatnip_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, kDTscatnip_2d, 0}
+#define intodumb_2d {kVInto_2d, 0, 0, 0, 0, 0, 0, kALdumb_2d}
+#define knock_2d {kVKnock_2d, 0, 0, DONT_CARE, 0, 0, kDTtnoknock_2d, 0}
+#define lightdynamite_2d {kVStrike_2d, kRmatch_2d, kDTnomatch_2d, DONT_CARE, 0, 0, 0, kALlightdynamite_2d}
+#define lookcubp_2d {kVLook_2d, 0, 0, DONT_CARE, 0, 0, 0, kALphoto_2d}
+#define lookgarlic_2d {kVLook_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindclove_2d, kALgetgarlic_2d}
+#define lookhole_2d {kVLook_2d, 0, 0, 0, 0, kDTdarkhole_2d, 0, kALkeyhole_2d}
+#define lookkennel_2d {kVLook_2d, 0, 0, 0, 0, 0, 0, kALlookkennel_2d}
+#define lookmat_2d {kVLook_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindmatch_2d, kALgetmatch_2d}
+#define lookmatch_2d {kVLook_2d, 0, 0, DONT_CARE, 0, 0, 0, kALlookmatch_2d}
+#define opencubp_2d {kVOpen_2d, 0, 0, DONT_CARE, 0, 0, 0, kALphoto_2d}
+#define opendoor1_2d {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor1_2d}
+#define opendoor2_2d {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor2_2d}
+#define opendoor3_2d {kVOpen_2d, 0, 0, 0, 1, kDTsopen1_2d, 0, kALopendoor3_2d}
+#define opengarlic_2d {kVOpen_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindclove_2d, kALgetgarlic_2d}
+#define openkdoor_2d {kVOpen_2d, 0, 0, 0, 0, 0, kDTwontopen_2d, 0}
+#define openlamp_2d {kVOpen_2d, kRlamp_2d, kDTnocgen_2d, DONT_CARE, 0, 0, kDTempty_2d, 0}
+#define openmat_2d {kVOpen_2d, 0, 0, 0, 1, kDTempty_2d, kDTfindmatch_2d, kALgetmatch_2d}
+#define openpdoor_2d {kVOpen_2d, 0, 0, 0, 0, 0, kDTspdoor_2d, 0}
+#define opensafe_2d {kVOpen_2d, 0, 0, DONT_CARE, 0, 0, 0, kALsafe_2d}
+#define popballoon_2d {kVBreak_2d, 0, 0, 0, 0, 0, 0, kALballoon_2d}
+#define pushblue_2d {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALworkgates_2d}
+#define pushbutton_2d {kVPush_2d, 0, 0, DONT_CARE, 0, 0, kDTtnopushbutton_2d, 0}
+#define pushgreen_2d {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALbugzapper_2d}
+#define pushpaper_2d {kVPush_2d, kRpaper_2d, kDTnocgen_2d, 0, 1, 0, kDTokgen_2d, kALpushpaper_2d}
+#define pushpencil_2d {kVPush_2d, kRpencil_2d, kDTnocgen_2d, 0, 0, 0, 0, kALpushpencil_2d}
+#define pushred_2d {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALshedlight_2d}
+#define pushyellow_2d {kVPush_2d, 0, 0, DONT_CARE, 0, 0, 0, kALgatelight_2d}
+#define readletter_2d {kVRead_2d, 0, 0, 3, 3, kDTsnoread_2d, kDTsread_2d, kALreadlet_2d}
+#define readwill_2d {kVRead_2d, kRwill_2d, kDTnocgen_2d, 1, 1, 0, 0, kALwill_2d}
+#define ringbell_2d {kVRing_2d, kRbell_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALbell_2d}
+#define rubcatnip_2d {kVRub_2d, kRcatnip_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, 0, kALcatnip_2d}
+#define rublamp_2d {kVRub_2d, kRlamp_2d, kDTnocgen_2d, 0, 0, kDTnopurps_2d, 0, kALlamp_2d}
+#define serum_2d {kVDrink_2d, kRserum_2d, kDTnocgen_2d, 0, 1, kDTsnosee_2d, 0, kALbottle_2d}
+#define strikematch_2d {kVStrike_2d, kRmatch_2d, kDTnocgen_2d, DONT_CARE, 0, 0, 0, kALstrikematch_2d}
+#define talkharry_2d {kVTalk_2d, 0, 0, 0, 1, kDTsharry_2d, 0, kALharry_2d}
+#define throwstick_2d {kVThrow_2d, kRstick_2d, kDTnocgen_2d, 0, 1, 0, 0, kALthrowstick_2d}
+#define unlock_2d {kVUnlock_2d, 0, 0, DONT_CARE, 0, 0, kDTtnounlock_2d, 0}
cmd balloon_2d[] = {popballoon_2d, getballoon_2d, emptyCmd};
cmd banana_2d[] = {givebanana_2d, eatbanana_2d, emptyCmd};
@@ -5022,49 +4792,49 @@ const cmd *cmdList_2d[] = {
well_2d, will_2d, yellow_2d
};
-cmd bell_3d = {kVRing_3d, kRbell_3d, kDTnocgen_3d, DONT_CARE, 0, 0, kDTokbell_3d, 0};
-cmd blow_3d = {kVBlow_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdart_3d};
-cmd book_3d = {kVRead_3d, kRbook_3d, kDTnocgen_3d, DONT_CARE, 0, 0, 0, kALreadbook_3d};
-cmd cage1_3d = {kVOpen_3d, 0, 0, DONT_CARE, 0, 0, 0, kALopencage_3d};
-cmd cage2_3d = {kVClose_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, 0};
-cmd cage3_3d = {kVTake_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtakecage_3d};
-cmd candle1_3d = {kVDouse_3d, 0, 0, 1, 0, kDTsunlit_3d, kDTokgen_3d, 0};
-cmd candle2_3d = {kVStrike_3d, 0, 0, 0, 1, kDTslit_3d, kDTokgen_3d, 0};
-cmd cdoor1_3d = {kVOpen_3d, 0, 0, 0, 0, kDTsopen1_3d, 0, kALopendoor_3d};
-cmd cdoor2_3d = {kVClose_3d, 0, 0, 1, 0, kDTsclose_3d, kDTokgen_3d, kALclosedoor_3d};
-cmd cexit1_3d = {kVOutof_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALexit_3d};
-cmd cexit2_3d = {kVClimb_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALexit_3d};
-cmd cflask1_3d = {kVFill_3d, 0, 0, 0, 1, kDTsfull_3d, 0, kALfill_3d};
-cmd cflask2_3d = {kVPut_3d, 0, 0, 0, 1, kDTsfull_3d, 0, kALfill_3d};
-cmd cflask3_3d = {kVEmpty_3d, 0, 0, DONT_CARE, 0, 0, 0, kALempty_3d};
-cmd cflask4_3d = {kVDrink_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdrink_3d};
-cmd cflask5_3d = {kVGive_3d, 0, 0, DONT_CARE, 0, 0, 0, kALflask_3d};
-cmd cheese1_3d = {kVEat_3d, kRcheese_3d, kDTnocgen_3d, 0, 0, 0, 0, kALeatcheese_3d};
-cmd cheese2_3d = {kVDrop_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdropcheese_3d};
-cmd cheese3_3d = {kVPut_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdropcheese_3d};
-cmd cheese4_3d = {kVTake_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtakecheese_3d};
-cmd cmake1_3d = {kVMake_3d, 0, 0, 0, 0, kDTsmade_3d, 0, kALmakeclay_3d};
-cmd cmake2_3d = {kVStick_3d, kRpins_3d, kDTnopins_3d, DONT_CARE, 0, 0, 0, kALstick_3d};
-cmd cplane1_3d = {kVClimb_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d};
-cmd cplane2_3d = {kVInto_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d};
-cmd cplane3_3d = {kVSearch_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d};
-cmd crystal_3d = {kVRub_3d, kRcrystal_3d, kDTnocgen_3d, DONT_CARE, 0, 0, 0, kALcrystal_3d};
-cmd cstick1_3d = {kVStick_3d, 0, 0, 0, 0, 0, 0, kALstick_3d};
-cmd cswing1_3d = {kVSwing_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALswing_3d};
-cmd ctalknat_3d = {kVTalk_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtalknat_3d};
-cmd cube1_3d = {kVGive_3d, 0, 0, DONT_CARE, 0, 0, 0, kALgiveb_3d};
-cmd cvine1_3d = {kVUntie_3d, 0, 0, 0, 0, 0, 0, kALuntie_vine_3d};
-cmd cvine2_3d = {kVTie_3d, 0, 0, 0, 0, 0, 0, kALvine_3d};
-cmd cwaterfall_3d = {kVLook_3d, 0, 0, DONT_CARE, 0, 0, 0, kALlookwfall_3d};
-cmd cwaterpool_3d = {kVLook_3d, 0, 0, DONT_CARE, 0, 0, kDTdull_3d, 0};
-cmd cwaterstream_3d = {kVLook_3d, 0, 0, DONT_CARE, 0, 0, kDTdull_3d, 0};
-cmd dart_3d = {kVShoot_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdart_3d};
-cmd ghost_3d = {kVExorcise_3d, kRexor_3d, kDTnocex_3d, DONT_CARE, 0, 0, 0, kALexorcise_3d};
-cmd knock_3d = {kVKnock_3d, 0, 0, DONT_CARE, 0, 0, kDTsNobody_3d, 0};
-cmd readit_3d = {kVRead_3d, 0, 0, DONT_CARE, 0, 0, kDTsread_3d, 0};
-cmd rock1_3d = {kVBehind_3d, 0, 0, 0, 1, kDTsfoundc_3d, kDTsfindc_3d, kALfindcrystal_3d};
-cmd swingc_3d = {kVSwing_3d, 0, 0, DONT_CARE, 0, 0, kDTswingcave_3d, 0};
-cmd unlock_3d = {kVUnlock_3d, 0, 0, DONT_CARE, 0, 0, kDTsUnlocked_3d, 0};
+#define bell_3d {kVRing_3d, kRbell_3d, kDTnocgen_3d, DONT_CARE, 0, 0, kDTokbell_3d, 0}
+#define blow_3d {kVBlow_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdart_3d}
+#define book_3d {kVRead_3d, kRbook_3d, kDTnocgen_3d, DONT_CARE, 0, 0, 0, kALreadbook_3d}
+#define cage1_3d {kVOpen_3d, 0, 0, DONT_CARE, 0, 0, 0, kALopencage_3d}
+#define cage2_3d {kVClose_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, 0}
+#define cage3_3d {kVTake_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtakecage_3d}
+#define candle1_3d {kVDouse_3d, 0, 0, 1, 0, kDTsunlit_3d, kDTokgen_3d, 0}
+#define candle2_3d {kVStrike_3d, 0, 0, 0, 1, kDTslit_3d, kDTokgen_3d, 0}
+#define cdoor1_3d {kVOpen_3d, 0, 0, 0, 0, kDTsopen1_3d, 0, kALopendoor_3d}
+#define cdoor2_3d {kVClose_3d, 0, 0, 1, 0, kDTsclose_3d, kDTokgen_3d, kALclosedoor_3d}
+#define cexit1_3d {kVOutof_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALexit_3d}
+#define cexit2_3d {kVClimb_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALexit_3d}
+#define cflask1_3d {kVFill_3d, 0, 0, 0, 1, kDTsfull_3d, 0, kALfill_3d}
+#define cflask2_3d {kVPut_3d, 0, 0, 0, 1, kDTsfull_3d, 0, kALfill_3d}
+#define cflask3_3d {kVEmpty_3d, 0, 0, DONT_CARE, 0, 0, 0, kALempty_3d}
+#define cflask4_3d {kVDrink_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdrink_3d}
+#define cflask5_3d {kVGive_3d, 0, 0, DONT_CARE, 0, 0, 0, kALflask_3d}
+#define cheese1_3d {kVEat_3d, kRcheese_3d, kDTnocgen_3d, 0, 0, 0, 0, kALeatcheese_3d}
+#define cheese2_3d {kVDrop_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdropcheese_3d}
+#define cheese3_3d {kVPut_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdropcheese_3d}
+#define cheese4_3d {kVTake_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtakecheese_3d}
+#define cmake1_3d {kVMake_3d, 0, 0, 0, 0, kDTsmade_3d, 0, kALmakeclay_3d}
+#define cmake2_3d {kVStick_3d, kRpins_3d, kDTnopins_3d, DONT_CARE, 0, 0, 0, kALstick_3d}
+#define cplane1_3d {kVClimb_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d}
+#define cplane2_3d {kVInto_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d}
+#define cplane3_3d {kVSearch_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALplane_3d}
+#define crystal_3d {kVRub_3d, kRcrystal_3d, kDTnocgen_3d, DONT_CARE, 0, 0, 0, kALcrystal_3d}
+#define cstick1_3d {kVStick_3d, 0, 0, 0, 0, 0, 0, kALstick_3d}
+#define cswing1_3d {kVSwing_3d, 0, 0, DONT_CARE, 0, 0, kDTokgen_3d, kALswing_3d}
+#define ctalknat_3d {kVTalk_3d, 0, 0, DONT_CARE, 0, 0, 0, kALtalknat_3d}
+#define cube1_3d {kVGive_3d, 0, 0, DONT_CARE, 0, 0, 0, kALgiveb_3d}
+#define cvine1_3d {kVUntie_3d, 0, 0, 0, 0, 0, 0, kALuntie_vine_3d}
+#define cvine2_3d {kVTie_3d, 0, 0, 0, 0, 0, 0, kALvine_3d}
+#define cwaterfall_3d {kVLook_3d, 0, 0, DONT_CARE, 0, 0, 0, kALlookwfall_3d}
+#define cwaterpool_3d {kVLook_3d, 0, 0, DONT_CARE, 0, 0, kDTdull_3d, 0}
+#define cwaterstream_3d {kVLook_3d, 0, 0, DONT_CARE, 0, 0, kDTdull_3d, 0}
+#define dart_3d {kVShoot_3d, 0, 0, DONT_CARE, 0, 0, 0, kALdart_3d}
+#define ghost_3d {kVExorcise_3d, kRexor_3d, kDTnocex_3d, DONT_CARE, 0, 0, 0, kALexorcise_3d}
+#define knock_3d {kVKnock_3d, 0, 0, DONT_CARE, 0, 0, kDTsNobody_3d, 0}
+#define readit_3d {kVRead_3d, 0, 0, DONT_CARE, 0, 0, kDTsread_3d, 0}
+#define rock1_3d {kVBehind_3d, 0, 0, 0, 1, kDTsfoundc_3d, kDTsfindc_3d, kALfindcrystal_3d}
+#define swingc_3d {kVSwing_3d, 0, 0, DONT_CARE, 0, 0, kDTswingcave_3d, 0}
+#define unlock_3d {kVUnlock_3d, 0, 0, DONT_CARE, 0, 0, kDTsUnlocked_3d, 0}
cmd cbell_3d[] = {bell_3d, emptyCmd};
cmd cbook_3d[] = {book_3d, emptyCmd};
diff --git a/tools/create_hugo/staticfont.h b/tools/create_hugo/staticfont.h
new file mode 100644
index 0000000000..c5700cdf58
--- /dev/null
+++ b/tools/create_hugo/staticfont.h
@@ -0,0 +1,187 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 STATICFONT_H
+#define STATICFONT_H
+
+byte font5[] = {
+ 6, 7, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7,
+ 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0,
+ 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 5, 1,
+ 128, 128, 128, 0, 128, 2, 3, 160, 160, 3, 3, 160, 0, 160, 5, 3, 64, 224, 64, 224,
+ 64, 5, 3, 160, 32, 64, 128, 160, 5, 3, 64, 160, 64, 160, 96, 2, 2, 64, 128, 5,
+ 2, 64, 128, 128, 128, 64, 5, 2, 128, 64, 64, 64, 128, 4, 3, 0, 160, 64, 160, 4,
+ 3, 0, 64, 224, 64, 5, 2, 0, 0, 0, 64, 128, 3, 3, 0, 0, 224, 5, 1, 0,
+ 0, 0, 0, 128, 5, 3, 32, 32, 64, 128, 128, 5, 3, 224, 160, 160, 160, 224, 5, 1,
+ 128, 128, 128, 128, 128, 5, 3, 224, 32, 224, 128, 224, 5, 3, 224, 32, 96, 32, 224, 5,
+ 3, 160, 160, 224, 32, 32, 5, 3, 224, 128, 224, 32, 224, 5, 3, 224, 128, 224, 160, 224,
+ 5, 3, 224, 32, 64, 64, 64, 5, 3, 224, 160, 224, 160, 224, 5, 3, 224, 160, 224, 32,
+ 224, 4, 2, 0, 128, 0, 128, 5, 2, 0, 64, 0, 64, 128, 5, 3, 32, 64, 128, 64,
+ 32, 4, 3, 0, 224, 0, 224, 5, 3, 128, 64, 32, 64, 128, 5, 3, 192, 32, 64, 0,
+ 64, 5, 3, 64, 160, 160, 128, 96, 5, 3, 64, 160, 224, 160, 160, 5, 3, 192, 160, 192,
+ 160, 192, 5, 3, 96, 128, 128, 128, 96, 5, 3, 192, 160, 160, 160, 192, 5, 3, 224, 128,
+ 192, 128, 224, 5, 3, 224, 128, 192, 128, 128, 5, 3, 224, 128, 160, 160, 224, 5, 3, 160,
+ 160, 224, 160, 160, 5, 3, 224, 64, 64, 64, 224, 5, 3, 32, 32, 32, 160, 64, 5, 3,
+ 160, 160, 192, 160, 160, 5, 3, 128, 128, 128, 128, 224, 5, 3, 160, 224, 224, 160, 160, 5,
+ 3, 160, 224, 224, 224, 160, 5, 3, 64, 160, 160, 160, 64, 5, 3, 192, 160, 192, 128, 128,
+ 5, 3, 64, 160, 160, 64, 32, 5, 3, 192, 160, 192, 160, 160, 5, 3, 96, 128, 64, 32,
+ 192, 5, 3, 224, 64, 64, 64, 64, 5, 3, 160, 160, 160, 160, 224, 5, 3, 160, 160, 160,
+ 64, 64, 5, 3, 160, 160, 224, 224, 160, 5, 3, 160, 160, 64, 160, 160, 5, 3, 160, 160,
+ 96, 32, 192, 5, 3, 224, 32, 64, 128, 224, 5, 2, 192, 128, 128, 128, 192, 5, 3, 128,
+ 128, 64, 32, 32, 5, 2, 192, 64, 64, 64, 192, 2, 3, 64, 160, 5, 3, 0, 0, 0,
+ 0, 224, 2, 2, 128, 64, 5, 3, 64, 160, 224, 160, 160, 5, 3, 192, 160, 192, 160, 192,
+ 5, 3, 96, 128, 128, 128, 96, 5, 3, 192, 160, 160, 160, 192, 5, 3, 224, 128, 192, 128,
+ 224, 5, 3, 224, 128, 192, 128, 128, 5, 3, 224, 128, 160, 160, 224, 5, 3, 160, 160, 224,
+ 160, 160, 5, 3, 224, 64, 64, 64, 224, 5, 3, 32, 32, 32, 160, 64, 5, 3, 160, 160,
+ 192, 160, 160, 5, 3, 128, 128, 128, 128, 224, 5, 3, 160, 224, 224, 160, 160, 5, 3, 160,
+ 224, 224, 224, 160, 5, 3, 64, 160, 160, 160, 64, 5, 3, 192, 160, 192, 128, 128, 5, 3,
+ 64, 160, 160, 64, 32, 5, 3, 192, 160, 192, 160, 160, 5, 3, 96, 128, 64, 32, 192, 5,
+ 3, 224, 64, 64, 64, 64, 5, 3, 160, 160, 160, 160, 224, 5, 3, 160, 160, 160, 64, 64,
+ 5, 3, 160, 160, 224, 224, 160, 5, 3, 160, 160, 64, 160, 160, 5, 3, 160, 160, 96, 32,
+ 192, 5, 3, 224, 32, 64, 128, 224, 5, 3, 96, 64, 192, 64, 96, 5, 1, 128, 128, 0,
+ 128, 128, 5, 3, 192, 64, 96, 64, 192, 1, 2, 64, 6, 1, 0, 0, 0, 0, 0, 0
+};
+
+byte font6[] = {
+ 11, 11, 7, 6, 4, 12, 12, 24, 216, 112, 48, 7, 8, 0, 35, 99, 255, 96, 32, 0,
+ 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6,
+ 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0,
+ 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0,
+ 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0,
+ 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7,
+ 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0,
+ 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0,
+ 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0,
+ 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0,
+ 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6,
+ 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0,
+ 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0,
+ 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 0, 0, 0, 0, 0, 7, 2, 192, 192, 192, 192, 0, 192, 192, 3,
+ 7, 102, 102, 204, 5, 5, 80, 248, 80, 248, 80, 7, 6, 48, 124, 208, 120, 44, 248, 48,
+ 7, 7, 194, 198, 12, 24, 48, 102, 198, 7, 6, 112, 216, 112, 116, 216, 216, 108, 3, 3,
+ 96, 96, 192, 7, 3, 96, 192, 192, 192, 192, 192, 96, 7, 3, 192, 96, 96, 96, 96, 96,
+ 192, 5, 5, 0, 0, 80, 32, 80, 6, 6, 0, 48, 48, 252, 48, 48, 8, 3, 0, 0,
+ 0, 0, 0, 96, 96, 192, 4, 6, 0, 0, 0, 120, 7, 2, 0, 0, 0, 0, 0, 192,
+ 192, 7, 7, 2, 6, 12, 24, 48, 96, 192, 7, 6, 120, 204, 204, 204, 204, 204, 120, 7,
+ 4, 96, 224, 96, 96, 96, 96, 240, 7, 6, 120, 204, 12, 56, 96, 192, 252, 7, 6, 120,
+ 204, 12, 56, 12, 204, 120, 7, 6, 204, 204, 204, 252, 12, 12, 12, 7, 6, 252, 192, 192,
+ 248, 12, 204, 120, 7, 6, 60, 96, 192, 248, 204, 204, 120, 7, 6, 252, 12, 12, 24, 24,
+ 48, 48, 7, 6, 120, 204, 204, 120, 204, 204, 120, 7, 6, 120, 204, 204, 124, 12, 24, 48,
+ 7, 2, 0, 192, 192, 0, 192, 192, 0, 7, 3, 0, 96, 96, 0, 96, 96, 192, 7, 5,
+ 24, 48, 96, 192, 96, 48, 24, 5, 6, 0, 0, 120, 0, 120, 7, 5, 192, 96, 48, 24,
+ 48, 96, 192, 7, 6, 120, 204, 12, 24, 48, 0, 48, 7, 6, 120, 204, 204, 220, 216, 192,
+ 124, 7, 6, 120, 204, 204, 252, 204, 204, 204, 7, 6, 248, 204, 204, 248, 204, 204, 248, 7,
+ 6, 120, 204, 192, 192, 192, 204, 120, 7, 6, 248, 204, 204, 204, 204, 204, 248, 7, 6, 252,
+ 192, 192, 248, 192, 192, 252, 7, 6, 252, 192, 192, 248, 192, 192, 192, 7, 6, 120, 204, 192,
+ 220, 204, 204, 120, 7, 6, 204, 204, 204, 252, 204, 204, 204, 7, 6, 252, 48, 48, 48, 48,
+ 48, 252, 7, 6, 12, 12, 12, 12, 12, 204, 120, 7, 6, 204, 204, 216, 240, 216, 204, 204,
+ 7, 6, 192, 192, 192, 192, 192, 192, 252, 7, 8, 195, 231, 255, 219, 219, 195, 195, 7, 8,
+ 195, 227, 243, 219, 207, 199, 195, 7, 6, 120, 204, 204, 204, 204, 204, 120, 7, 6, 248, 204,
+ 204, 248, 192, 192, 192, 7, 6, 120, 204, 204, 204, 204, 216, 108, 7, 6, 248, 204, 204, 248,
+ 240, 216, 204, 7, 6, 120, 204, 192, 120, 12, 204, 120, 7, 6, 252, 48, 48, 48, 48, 48,
+ 48, 7, 6, 204, 204, 204, 204, 204, 204, 120, 7, 6, 204, 204, 204, 204, 204, 120, 48, 7,
+ 8, 195, 195, 195, 219, 219, 255, 102, 7, 6, 204, 204, 120, 48, 120, 204, 204, 7, 6, 204,
+ 204, 204, 120, 48, 48, 48, 7, 6, 252, 12, 24, 48, 96, 192, 252, 7, 4, 240, 192, 192,
+ 192, 192, 192, 240, 7, 7, 128, 192, 96, 48, 24, 12, 6, 7, 4, 240, 48, 48, 48, 48,
+ 48, 240, 3, 6, 48, 120, 204, 10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 3,
+ 3, 192, 192, 96, 7, 6, 0, 0, 120, 204, 204, 220, 108, 7, 6, 192, 192, 248, 204, 204,
+ 204, 248, 7, 6, 0, 0, 120, 204, 192, 204, 120, 7, 6, 12, 12, 124, 204, 204, 204, 124,
+ 7, 6, 0, 0, 120, 204, 248, 192, 124, 7, 6, 120, 204, 192, 240, 192, 192, 192, 10, 6,
+ 0, 0, 124, 204, 204, 220, 108, 12, 204, 120, 7, 6, 192, 192, 216, 236, 204, 204, 204, 7,
+ 2, 192, 0, 192, 192, 192, 192, 192, 10, 6, 12, 0, 12, 12, 12, 12, 12, 12, 204, 120,
+ 7, 6, 192, 192, 204, 216, 240, 216, 204, 7, 2, 192, 192, 192, 192, 192, 192, 192, 7, 10,
+ 0, 0, 0, 0, 219, 128, 238, 192, 204, 192, 204, 192, 204, 192, 7, 6, 0, 0, 248, 204,
+ 204, 204, 204, 7, 6, 0, 0, 120, 204, 204, 204, 120, 10, 6, 0, 0, 248, 204, 204, 204,
+ 248, 192, 192, 192, 10, 6, 0, 0, 124, 204, 204, 220, 108, 12, 12, 12, 7, 6, 0, 0,
+ 248, 204, 192, 192, 192, 7, 6, 0, 0, 124, 192, 120, 12, 248, 7, 4, 96, 96, 240, 96,
+ 96, 96, 96, 7, 6, 0, 0, 204, 204, 204, 204, 120, 7, 6, 0, 0, 204, 204, 204, 120,
+ 48, 7, 8, 0, 0, 195, 195, 219, 255, 102, 7, 6, 0, 0, 204, 120, 48, 120, 204, 10,
+ 6, 0, 0, 204, 204, 204, 204, 124, 12, 204, 120, 7, 6, 0, 0, 252, 24, 48, 96, 252,
+ 7, 4, 112, 192, 96, 240, 96, 192, 112, 7, 2, 192, 192, 192, 0, 192, 192, 192, 7, 5,
+ 224, 48, 96, 248, 96, 48, 224, 7, 7, 0, 0, 96, 146, 12, 0, 0, 7, 6, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+byte font8[] = {
+ 9, 8, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0,
+ 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2,
+ 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1,
+ 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0,
+ 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3, 0, 7, 7,
+ 192, 192, 192, 192, 0, 192, 192, 2, 3, 160, 160, 5, 5, 80, 248, 80, 248, 80, 7, 7,
+ 40, 126, 168, 124, 42, 252, 40, 7, 7, 194, 196, 8, 16, 32, 70, 134, 1, 7, 0, 2,
+ 2, 64, 128, 7, 3, 96, 192, 192, 192, 192, 192, 96, 7, 3, 192, 96, 96, 96, 96, 96,
+ 192, 6, 6, 0, 72, 48, 252, 48, 72, 6, 5, 0, 32, 32, 248, 32, 32, 8, 3, 0,
+ 0, 0, 0, 0, 96, 96, 192, 4, 5, 0, 0, 0, 248, 7, 2, 0, 0, 0, 0, 0,
+ 192, 192, 7, 7, 2, 4, 8, 16, 32, 64, 128, 7, 6, 120, 204, 204, 204, 204, 204, 120,
+ 7, 3, 96, 224, 96, 96, 96, 96, 96, 7, 7, 124, 198, 12, 24, 48, 96, 254, 7, 7,
+ 124, 198, 6, 28, 6, 198, 124, 7, 7, 198, 198, 198, 254, 6, 6, 6, 7, 7, 254, 192,
+ 192, 252, 6, 198, 124, 7, 7, 124, 198, 192, 252, 198, 198, 124, 7, 7, 254, 6, 6, 12,
+ 12, 24, 24, 7, 7, 124, 198, 198, 124, 198, 198, 124, 7, 7, 124, 198, 198, 126, 6, 198,
+ 124, 6, 2, 0, 192, 192, 0, 192, 192, 7, 3, 0, 96, 96, 0, 96, 96, 192, 7, 5,
+ 24, 48, 96, 192, 96, 48, 24, 5, 5, 0, 0, 248, 0, 248, 7, 5, 192, 96, 48, 24,
+ 48, 96, 192, 7, 7, 124, 198, 6, 12, 24, 0, 24, 1, 7, 0, 7, 7, 124, 198, 198,
+ 254, 198, 198, 198, 7, 7, 252, 198, 198, 252, 198, 198, 252, 7, 7, 124, 198, 192, 192, 192,
+ 198, 124, 7, 7, 252, 198, 198, 198, 198, 198, 252, 7, 7, 254, 192, 192, 248, 192, 192, 254,
+ 7, 7, 254, 192, 192, 248, 192, 192, 192, 7, 7, 124, 198, 192, 206, 198, 198, 124, 7, 7,
+ 198, 198, 198, 254, 198, 198, 198, 7, 6, 252, 48, 48, 48, 48, 48, 252, 7, 7, 6, 6,
+ 6, 6, 6, 198, 124, 7, 7, 198, 198, 204, 248, 204, 198, 198, 7, 7, 192, 192, 192, 192,
+ 192, 192, 254, 7, 7, 198, 238, 254, 214, 198, 198, 198, 7, 7, 198, 230, 246, 214, 222, 206,
+ 198, 7, 7, 124, 198, 198, 198, 198, 198, 124, 7, 7, 252, 198, 198, 252, 192, 192, 192, 7,
+ 7, 124, 198, 198, 198, 198, 204, 118, 7, 7, 252, 198, 198, 252, 204, 198, 198, 7, 7, 126,
+ 192, 192, 124, 6, 6, 252, 7, 6, 252, 48, 48, 48, 48, 48, 48, 7, 7, 198, 198, 198,
+ 198, 198, 198, 124, 7, 7, 198, 198, 198, 108, 108, 56, 16, 7, 7, 198, 198, 198, 214, 254,
+ 238, 198, 7, 7, 198, 198, 108, 56, 108, 198, 198, 7, 7, 204, 204, 204, 120, 48, 48, 48,
+ 7, 6, 252, 12, 24, 48, 96, 192, 252, 7, 3, 224, 192, 192, 192, 192, 192, 224, 7, 7,
+ 128, 64, 32, 16, 8, 4, 2, 7, 3, 224, 96, 96, 96, 96, 96, 224, 3, 5, 32, 80,
+ 136, 8, 7, 0, 0, 0, 0, 0, 0, 0, 254, 2, 2, 128, 64, 7, 7, 0, 0, 124,
+ 198, 198, 198, 126, 7, 7, 192, 192, 252, 198, 198, 198, 252, 7, 7, 0, 0, 124, 198, 192,
+ 198, 124, 7, 7, 6, 6, 126, 198, 198, 198, 126, 7, 7, 0, 0, 124, 198, 254, 192, 126,
+ 7, 6, 120, 204, 192, 240, 192, 192, 192, 8, 7, 0, 0, 124, 198, 198, 126, 6, 124, 7,
+ 7, 192, 192, 220, 230, 198, 198, 198, 7, 2, 0, 192, 0, 192, 192, 192, 192, 8, 6, 0,
+ 12, 0, 12, 12, 12, 204, 120, 7, 7, 192, 192, 198, 204, 248, 204, 198, 7, 2, 192, 192,
+ 192, 192, 192, 192, 192, 7, 7, 0, 0, 252, 214, 214, 214, 198, 7, 7, 0, 0, 220, 230,
+ 198, 198, 198, 7, 7, 0, 0, 124, 198, 198, 198, 124, 8, 7, 0, 0, 124, 198, 198, 252,
+ 192, 192, 8, 7, 0, 0, 124, 198, 198, 126, 6, 6, 7, 7, 0, 0, 220, 230, 192, 192,
+ 192, 7, 7, 0, 0, 126, 192, 124, 6, 252, 7, 7, 96, 248, 96, 96, 96, 102, 60, 7,
+ 7, 0, 0, 198, 198, 198, 198, 124, 7, 7, 0, 0, 198, 198, 108, 56, 16, 7, 7, 0,
+ 0, 198, 198, 214, 214, 124, 7, 7, 0, 0, 198, 108, 56, 108, 198, 8, 7, 0, 0, 198,
+ 198, 198, 126, 6, 252, 7, 6, 0, 0, 252, 24, 48, 96, 252, 7, 4, 48, 96, 96, 192,
+ 96, 96, 48, 7, 2, 192, 192, 192, 0, 192, 192, 192, 7, 4, 192, 96, 96, 48, 96, 96,
+ 192, 1, 2, 0, 1, 2, 0
+};
+
+#endif //STATICFONT_H
diff --git a/tools/create_hugo/staticintro.h b/tools/create_hugo/staticintro.h
index f9e59952e7..2fe18b8231 100644
--- a/tools/create_hugo/staticintro.h
+++ b/tools/create_hugo/staticintro.h
@@ -33,24 +33,49 @@
#ifndef STATICINTRO_H
#define STATICINTRO_H
-#define NUM_INTRO_TEXT 3
-#define NUM_INTRO_TICK 36
+#define NUM_INTRO_TEXT_DUMMY 1
+#define NUM_INTRO_TEXT_V3 3
+
+// Hugo1 DOS have 11 intro ticks, Hugo3 DOS and Hugo3 have 36
+#define NUM_INTRO_TICK_DUMMY 1
+#define NUM_INTRO_TICK_V1D 11
+#define NUM_INTRO_TICK_V3 36
+
// We use intro_tick as an index into the following coordinate list for the plane path.
-const byte x_intro[NUM_INTRO_TICK] = {
+// This is only used in v3.
+// v1 Dos uses TICKS too, for displaying the texts at a specific pace. x and y arrays
+// are dummy
+const byte x_intro_dummy[] = { 0 };
+
+const byte x_intro_v1d[NUM_INTRO_TICK_V1D] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+const byte x_intro_v3[NUM_INTRO_TICK_V3] = {
210, 204, 198, 192, 186, 180, 174, 168, 162, 156,
152, 149, 152, 158, 165, 171, 170, 165, 161, 157,
150, 144, 138, 134, 133, 134, 138, 144, 146, 142,
137, 132, 128, 124, 120, 115
};
-const byte y_intro[NUM_INTRO_TICK] = {
+const byte y_intro_dummy[] = { 0 };
+
+const byte y_intro_v1d[NUM_INTRO_TICK_V1D] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+const byte y_intro_v3[NUM_INTRO_TICK_V3] = {
61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
63, 66, 71, 74, 72, 75, 80, 82, 83, 84,
84, 84, 85, 89, 94, 99, 103, 104, 100, 98,
100, 103, 106, 109, 111, 112
};
-const char *textIntro[NUM_INTRO_TEXT] = {
+// Only Hugo 3 uses texts during intro
+const char *textIntro_dummy[NUM_INTRO_TEXT_DUMMY] = {""};
+const char *textIntro_v3[NUM_INTRO_TEXT_V3] = {
"Hugo and Penelope are returning\nhome from their vacation at the\ncottage of Great Uncle Horace.",
"Suddenly, a freak magnetic storm\ncauses the compass in their light\naircraft to spin wildly! Unable\nto navigate, Hugo loses all sense\nof direction...",
"Finally, hopelessly lost over a\nSouth American Jungle, the plane\nabout to run out of gas, Hugo\nspots a clearing just big enough\nto land it.\n\nWith fingers clenching the controls\nhe shouts: Hold on Penelope, we're\ngoing down...!"
diff --git a/tools/create_hugo/staticparser.h b/tools/create_hugo/staticparser.h
index 95e790f40a..9e67e98c26 100644
--- a/tools/create_hugo/staticparser.h
+++ b/tools/create_hugo/staticparser.h
@@ -33,7 +33,7 @@
#ifndef STATICPARSER_H
#define STATICPARSER_H
-#define NUM_PARSER_TEXT 18
+#define NUM_PARSER_TEXT 25
const char *textParser[NUM_PARSER_TEXT] = {
"You should press ALT+F4 or click on Game/Exit.",
"You are in a maze of\ntwisty little paths,\nwhich are all alike!",
@@ -52,7 +52,14 @@ const char *textParser[NUM_PARSER_TEXT] = {
"I don't see any here!",
"You're not close enough!",
"You are carrying:",
- "\nPress ESCAPE to continue"
+ "\nPress ESCAPE to continue",
+ "I see nothing special about it",
+ "You don't have it!",
+ "I don't see it anywhere",
+ "Are you sure you want to QUIT?",
+ "Apparently our hero either doesn't\nunderstand what you mean or doesn't\nthink that would be very useful!",
+ "I find that befuddling!",
+ "I don't think that would\naccomplish much, somehow!"
};
#endif //STATICPARSER_H
diff --git a/tools/create_kyradat/create_kyradat.cpp b/tools/create_kyradat/create_kyradat.cpp
index 85038a0820..7a4b8d73a1 100644
--- a/tools/create_kyradat/create_kyradat.cpp
+++ b/tools/create_kyradat/create_kyradat.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
@@ -45,7 +48,7 @@
#include <map>
enum {
- kKyraDatVersion = 72
+ kKyraDatVersion = 73
};
const ExtractFilename extractFilenames[] = {
@@ -53,7 +56,7 @@ const ExtractFilename extractFilenames[] = {
{ kIdMap, -1, true },
// INTRO / OUTRO sequences
- { k1ForestSeq, kTypeRawData, false },
+ { k1ForestSeq, kTypeForestSeqData, false },
{ k1KallakWritingSeq, kTypeRawData, false },
{ k1KyrandiaLogoSeq, kTypeRawData, false },
{ k1KallakMalcolmSeq, kTypeRawData, false },
diff --git a/tools/create_kyradat/extract.cpp b/tools/create_kyradat/extract.cpp
index 8286d1ca63..87254b03cf 100644
--- a/tools/create_kyradat/extract.cpp
+++ b/tools/create_kyradat/extract.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "extract.h"
#include <algorithm>
@@ -36,6 +39,7 @@ bool extractStrings(PAKFile &out, const ExtractInformation *info, const byte *da
bool extractStrings10(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
bool extractRooms(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
bool extractShapes(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
+bool extractKyraForestSeqData(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
bool extractAmigaSfx(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
bool extractWdSfx(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id);
@@ -58,6 +62,7 @@ const ExtractType extractTypeTable[] = {
{ kTypeRoomList, extractRooms },
{ kTypeShapeList, extractShapes },
{ kTypeRawData, extractRaw },
+ { kTypeForestSeqData, extractKyraForestSeqData },
{ kTypeAmigaSfxTable, extractAmigaSfx },
{ kTypeTownsWDSfxTable, extractWdSfx },
@@ -90,6 +95,7 @@ const TypeTable typeTable[] = {
{ kTypeRawData, 1 },
{ kTypeRoomList, 2 },
{ kTypeShapeList, 3 },
+ { kTypeForestSeqData, 1 },
{ kTypeAmigaSfxTable, 4 },
{ kTypeTownsWDSfxTable, 1 },
{ k2TypeSeqData, 5 },
@@ -376,6 +382,55 @@ bool extractShapes(PAKFile &out, const ExtractInformation *info, const byte *dat
return out.addFile(filename, buffer, size + 1 * 4);
}
+bool extractKyraForestSeqData(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id) {
+ if (info->platform != kPlatformPC98)
+ return extractRaw(out, info, data, size, filename, id);
+
+ struct PatchEntry {
+ uint16 pos;
+ uint8 val;
+ };
+
+ // This data has been taken from the FM-Towns version
+ static const PatchEntry patchData[] = {
+ { 0x0019, 0x06 }, { 0x001A, 0x09 }, { 0x001B, 0x00 }, { 0x002E, 0x06 }, { 0x002F, 0x09 }, { 0x0030, 0x00 },
+ { 0x003D, 0x06 }, { 0x003E, 0x09 }, { 0x003F, 0x00 }, { 0x004C, 0x06 }, { 0x004D, 0x09 }, { 0x004E, 0x00 },
+ { 0x005B, 0x06 }, { 0x005C, 0x09 }, { 0x005D, 0x00 }, { 0x0064, 0x06 }, { 0x0065, 0x09 }, { 0x0066, 0x00 },
+ { 0x0079, 0x06 }, { 0x007A, 0x09 }, { 0x007B, 0x00 }, { 0x0088, 0x06 }, { 0x0089, 0x09 }, { 0x008A, 0x00 },
+ { 0x0097, 0x06 }, { 0x0098, 0x09 }, { 0x0099, 0x00 }, { 0x00A6, 0x06 }, { 0x00A7, 0x09 }, { 0x00A8, 0x00 },
+ { 0x00AD, 0x06 }, { 0x00AE, 0x09 }, { 0x00AF, 0x00 }, { 0x00B4, 0x06 }, { 0x00B5, 0x09 }, { 0x00B6, 0x00 },
+ { 0x00C3, 0x06 }, { 0x00C4, 0x09 }, { 0x00C5, 0x00 }, { 0x00CA, 0x06 }, { 0x00CB, 0x09 }, { 0x00CC, 0x00 },
+ { 0x00D1, 0x06 }, { 0x00D2, 0x09 }, { 0x00D3, 0x00 }, { 0x00E0, 0x06 }, { 0x00E1, 0x09 }, { 0x00E2, 0x00 },
+ { 0x00E7, 0x06 }, { 0x00E8, 0x09 }, { 0x00E9, 0x00 }, { 0x00EE, 0x06 }, { 0x00EF, 0x09 }, { 0x00F0, 0x00 },
+ { 0x00FD, 0x06 }, { 0x00FE, 0x09 }, { 0x00FF, 0x00 }, { 0x010A, 0x06 }, { 0x010B, 0x09 }, { 0x010C, 0x00 },
+ { 0x011D, 0x06 }, { 0x011E, 0x09 }, { 0x011F, 0x00 }, { 0x012C, 0x06 }, { 0x012D, 0x09 }, { 0x012E, 0x00 },
+ { 0x013D, 0x06 }, { 0x013E, 0x09 }, { 0x013F, 0x00 }, { 0x0148, 0x06 }, { 0x0149, 0x09 }, { 0x014A, 0x00 },
+ { 0x0153, 0x06 }, { 0x0154, 0x09 }, { 0x0155, 0x00 }, { 0x015E, 0x06 }, { 0x015F, 0x09 }, { 0x0160, 0x00 },
+ { 0x0169, 0x06 }, { 0x016A, 0x09 }, { 0x016B, 0x00 }, { 0x016C, 0x06 }, { 0x016D, 0x12 }, { 0x016E, 0x00 },
+ { 0x017B, 0x06 }, { 0x017C, 0x09 }, { 0x017D, 0x00 }, { 0x0188, 0x06 }, { 0x0189, 0x09 }, { 0x018A, 0x00 },
+ { 0x0190, 0x13 }, { 0x0000, 0x00 }
+ };
+
+ uint32 outsize = size + (ARRAYSIZE(patchData) - 1);
+ uint8 *buffer = new uint8[outsize];
+ assert(buffer);
+
+ const uint8 *src = data;
+ uint8 *dst = buffer;
+ const PatchEntry *patchPos = patchData;
+
+ while (dst < (buffer + outsize)) {
+ if ((dst - buffer) == patchPos->pos) {
+ *dst++ = patchPos->val;
+ patchPos++;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+
+ return out.addFile(filename, buffer, outsize);
+}
+
bool extractAmigaSfx(PAKFile &out, const ExtractInformation *info, const byte *data, const uint32 size, const char *filename, int id) {
const uint32 entries = size / 8;
byte *buffer = new byte[entries * 6 + 1 * 4];
diff --git a/tools/create_kyradat/extract.h b/tools/create_kyradat/extract.h
index 5aee089466..0903852dd2 100644
--- a/tools/create_kyradat/extract.h
+++ b/tools/create_kyradat/extract.h
@@ -35,6 +35,7 @@ enum kExtractType {
kTypeRoomList,
kTypeShapeList,
kTypeRawData,
+ kTypeForestSeqData,
kTypeAmigaSfxTable,
kTypeTownsWDSfxTable,
diff --git a/tools/create_kyradat/games.cpp b/tools/create_kyradat/games.cpp
index 008120868f..75a956cab0 100644
--- a/tools/create_kyradat/games.cpp
+++ b/tools/create_kyradat/games.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "create_kyradat.h"
// Game tables
diff --git a/tools/create_kyradat/md5.cpp b/tools/create_kyradat/md5.cpp
index 58e6e73add..214b5ef7ed 100644
--- a/tools/create_kyradat/md5.cpp
+++ b/tools/create_kyradat/md5.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "md5.h"
#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i)
diff --git a/tools/create_kyradat/pak.cpp b/tools/create_kyradat/pak.cpp
index f0564ebfbf..4179f42df0 100644
--- a/tools/create_kyradat/pak.cpp
+++ b/tools/create_kyradat/pak.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "pak.h"
bool PAKFile::loadFile(const char *file, const bool isAmiga) {
diff --git a/tools/create_kyradat/search.cpp b/tools/create_kyradat/search.cpp
index 7ca41e2d92..28631fa652 100644
--- a/tools/create_kyradat/search.cpp
+++ b/tools/create_kyradat/search.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "search.h"
#include "md5.h"
diff --git a/tools/create_kyradat/tables.cpp b/tools/create_kyradat/tables.cpp
index e2235b1b78..777f237650 100644
--- a/tools/create_kyradat/tables.cpp
+++ b/tools/create_kyradat/tables.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "tables.h"
#include "create_kyradat.h"
@@ -973,7 +976,7 @@ const ExtractEntrySearchData k1TownsMusicFadeTableProvider[] = {
EXTRACT_END_ENTRY
};
-
+
const ExtractEntrySearchData k1TownsSFXwdTableProvider[] = {
{ UNK_LANG, kPlatformFMTowns, { 0x00012608, 0x006717A1, { { 0x34, 0xDD, 0x2D, 0xA5, 0x14, 0x05, 0xEE, 0x2F, 0x93, 0x7C, 0x78, 0x4D, 0xCA, 0x13, 0xED, 0x93 } } } },
diff --git a/tools/create_kyradat/util.cpp b/tools/create_kyradat/util.cpp
index a9ac5517bf..702a36bc0e 100644
--- a/tools/create_kyradat/util.cpp
+++ b/tools/create_kyradat/util.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "util.h"
#include <stdarg.h>
diff --git a/tools/create_lure/create_lure_dat.cpp b/tools/create_lure/create_lure_dat.cpp
index faa32541bc..ad883b808f 100644
--- a/tools/create_lure/create_lure_dat.cpp
+++ b/tools/create_lure/create_lure_dat.cpp
@@ -27,6 +27,9 @@
* to work properly
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
diff --git a/tools/create_lure/create_lure_dat.h b/tools/create_lure/create_lure_dat.h
index 93da90d56c..4743f6ac3f 100644
--- a/tools/create_lure/create_lure_dat.h
+++ b/tools/create_lure/create_lure_dat.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef __CREATELURE_DAT__
-#define __CREATELURE_DAT__
+#ifndef CREATE_LURE_DAT_H
+#define CREATE_LURE_DAT_H
#include "common/endian.h"
#include "common/util.h"
diff --git a/tools/create_lure/process_actions.cpp b/tools/create_lure/process_actions.cpp
index 9779315ddd..30e7eed9e4 100644
--- a/tools/create_lure/process_actions.cpp
+++ b/tools/create_lure/process_actions.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "common/scummsys.h"
#include "common/util.h"
#include "create_lure_dat.h"
diff --git a/tools/create_msvc/create_msvc.cpp b/tools/create_msvc/create_msvc.cpp
index 1c395b01aa..07943a7b82 100644
--- a/tools/create_msvc/create_msvc.cpp
+++ b/tools/create_msvc/create_msvc.cpp
@@ -833,6 +833,8 @@ const Feature s_features[] = {
{ "mad", "USE_MAD", "libmad.lib", true, "libmad (MP3) support" },
{ "vorbis", "USE_VORBIS", "libvorbisfile_static.lib libvorbis_static.lib libogg_static.lib", true, "Ogg Vorbis support" },
{ "flac", "USE_FLAC", "libFLAC_static.lib", true, "FLAC support" },
+ { "theoradec", "USE_THEORADEC", "libtheora_static.lib", true, "Theora decoder support" },
+ { "png", "USE_PNG", "libpng.lib", true, "PNG support" },
{ "mpeg2", "USE_MPEG2", "libmpeg2.lib", false, "mpeg2 codec for cutscenes" },
// ScummVM feature flags
@@ -1601,6 +1603,17 @@ void VisualStudioProvider::createProjectFile(const std::string &name, const std:
"\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\">\n"
"\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableSpecificWarnings=\"" << warnings->second << "\" />\n"
"\t\t</Configuration>\n";
+ } else if (name == "sword25") {
+ // Win32
+ project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n"
+ "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n"
+ "\t\t</Configuration>\n"
+ "\t\t<Configuration Name=\"Release|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release.vsprops\" />\n";
+ // x64
+ project << "\t\t<Configuration Name=\"Debug|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug64.vsprops\">\n"
+ "\t\t\t<Tool Name=\"VCCLCompilerTool\" DisableLanguageExtensions=\"false\" />\n"
+ "\t\t</Configuration>\n"
+ "\t\t<Configuration Name=\"Release|x64\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Release64.vsprops\" />\n";
} else if (name == "tinsel") {
// Win32
project << "\t\t<Configuration Name=\"Debug|Win32\" ConfigurationType=\"4\" InheritedPropertySheets=\".\\ScummVM_Debug.vsprops\">\n"
@@ -1715,7 +1728,7 @@ void VisualStudioProvider::createBuildProp(const BuildSetup &setup, bool isRelea
if (isRelease) {
properties << "\t\tEnableIntrinsicFunctions=\"true\"\n"
"\t\tWholeProgramOptimization=\"true\"\n"
- "\t\tPreprocessorDefinitions=\"WIN32\"\n"
+ "\t\tPreprocessorDefinitions=\"WIN32;RELEASE_BUILD\"\n"
"\t\tStringPooling=\"true\"\n"
"\t\tBufferSecurityCheck=\"false\"\n"
"\t\tDebugInformationFormat=\"0\"\n"
@@ -2010,7 +2023,7 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s
std::map<std::string, std::string>::iterator warnings = _projectWarnings.find(name);
// Nothing to add here, move along!
- if (name != "scummvm" && name != "tinsel" && warnings == _projectWarnings.end())
+ if (name != "scummvm" && name != "sword25" && name != "tinsel" && warnings == _projectWarnings.end())
return;
project << "\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='" << (isRelease ? "Release" : "Debug") << "|" << (isWin32 ? "Win32" : "x64") << "'\">\n"
@@ -2020,6 +2033,9 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s
if (name == "scummvm") {
project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n";
} else {
+ if (name == "sword25")
+ project << "\t\t\t<DisableLanguageExtensions>false</DisableLanguageExtensions>\n";
+
if (name == "tinsel" && !isRelease)
project << "\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\n";
@@ -2045,7 +2061,7 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s
project << "\t</ItemDefinitionGroup>\n";
}
-void MSBuildProvider::outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32) {
+void MSBuildProvider::outputGlobalPropFile(std::ofstream &properties, int bits, const std::string &defines, const std::string &prefix, bool isWin32) {
properties << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n"
"\t<PropertyGroup>\n"
@@ -2111,7 +2127,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b
if (isRelease) {
properties << "\t\t\t<IntrinsicFunctions>true</IntrinsicFunctions>\n"
"\t\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\n"
- "\t\t\t<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n"
+ "\t\t\t<PreprocessorDefinitions>WIN32;RELEASE_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n"
"\t\t\t<StringPooling>true</StringPooling>\n"
"\t\t\t<BufferSecurityCheck>false</BufferSecurityCheck>\n"
"\t\t\t<DebugInformationFormat></DebugInformationFormat>\n"
diff --git a/tools/create_teenagent/create_teenagent.cpp b/tools/create_teenagent/create_teenagent.cpp
index 930f40581f..ae88cc5d65 100644
--- a/tools/create_teenagent/create_teenagent.cpp
+++ b/tools/create_teenagent/create_teenagent.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
diff --git a/tools/create_teenagent/md5.cpp b/tools/create_teenagent/md5.cpp
index 58e6e73add..214b5ef7ed 100644
--- a/tools/create_teenagent/md5.cpp
+++ b/tools/create_teenagent/md5.cpp
@@ -23,6 +23,9 @@
*
*/
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
#include "md5.h"
#define GET_UINT32(n, b, i) (n) = READ_LE_UINT32(b + i)
diff --git a/tools/create_toon/create_toon.cpp b/tools/create_toon/create_toon.cpp
new file mode 100644
index 0000000000..3cf252cbcd
--- /dev/null
+++ b/tools/create_toon/create_toon.cpp
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ * This is a utility for storing all the hardcoded data of Toonstruck in a separate
+ * data file, used by the game engine
+ */
+
+// Disable symbol overrides so that we can use system headers.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+// HACK to allow building with the SDL backend on MinGW
+// see bug #1800764 "TOOLS: MinGW tools building broken"
+#ifdef main
+#undef main
+#endif // main
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/scummsys.h"
+#include "common/events.h"
+
+#include "create_toon.h"
+#include "staticdata.h"
+
+static void writeByte(FILE *fp, uint8 b) {
+ fwrite(&b, 1, 1, fp);
+}
+
+static void writeUint16BE(FILE *fp, uint16 value) {
+ writeByte(fp, (uint8)(value >> 8));
+ writeByte(fp, (uint8)(value & 0xFF));
+}
+
+void writeSint16BE(FILE *fp, int16 value) {
+ writeUint16BE(fp, (uint16)value);
+}
+
+static void writeUint32BE(FILE *fp, uint32 value) {
+ writeByte(fp, (uint8)(value >> 24));
+ writeByte(fp, (uint8)((value >> 16) & 0xFF));
+ writeByte(fp, (uint8)((value >> 8) & 0xFF));
+ writeByte(fp, (uint8)(value & 0xFF));
+}
+
+void writeSint32BE(FILE *fp, int32 value) {
+ writeUint32BE(fp, (uint16)value);
+}
+
+int main(int argc, char *argv[]) {
+ FILE *outFile;
+ int nbrElem;
+
+ outFile = fopen("toon.dat", "wb");
+
+ // Write header
+ fwrite("TOON", 4, 1, outFile);
+
+ writeByte(outFile, TOON_DAT_VER_MAJ);
+ writeByte(outFile, TOON_DAT_VER_MIN);
+
+ // game versions/variantes
+ writeUint16BE(outFile, NUM_VARIANTE);
+
+ // Write locationDirNotVisited
+ nbrElem = sizeof(locationDirNotVisited_EN) / sizeof(char *);
+ writeTextArray(outFile, locationDirNotVisited_EN, nbrElem);
+
+ nbrElem = sizeof(locationDirNotVisited_FR) / sizeof(char *);
+ writeTextArray(outFile, locationDirNotVisited_FR, nbrElem);
+
+ nbrElem = sizeof(locationDirNotVisited_DE) / sizeof(char *);
+ writeTextArray(outFile, locationDirNotVisited_DE, nbrElem);
+
+ nbrElem = sizeof(locationDirNotVisited_RU) / sizeof(char *);
+ writeTextArray(outFile, locationDirNotVisited_RU, nbrElem);
+
+ nbrElem = sizeof(locationDirNotVisited_SP) / sizeof(char *);
+ writeTextArray(outFile, locationDirNotVisited_SP, nbrElem);
+
+ // Write locationDirVisited
+ nbrElem = sizeof(locationDirVisited_EN) / sizeof(char *);
+ writeTextArray(outFile, locationDirVisited_EN, nbrElem);
+
+ nbrElem = sizeof(locationDirVisited_FR) / sizeof(char *);
+ writeTextArray(outFile, locationDirVisited_FR, nbrElem);
+
+ nbrElem = sizeof(locationDirVisited_DE) / sizeof(char *);
+ writeTextArray(outFile, locationDirVisited_DE, nbrElem);
+
+ nbrElem = sizeof(locationDirVisited_RU) / sizeof(char *);
+ writeTextArray(outFile, locationDirVisited_RU, nbrElem);
+
+ nbrElem = sizeof(locationDirVisited_SP) / sizeof(char *);
+ writeTextArray(outFile, locationDirVisited_SP, nbrElem);
+
+ // Write specialInfoLine
+ nbrElem = sizeof(specialInfoLine_EN) / sizeof(char *);
+ writeTextArray(outFile, specialInfoLine_EN, nbrElem);
+
+ nbrElem = sizeof(specialInfoLine_FR) / sizeof(char *);
+ writeTextArray(outFile, specialInfoLine_FR, nbrElem);
+
+ nbrElem = sizeof(specialInfoLine_DE) / sizeof(char *);
+ writeTextArray(outFile, specialInfoLine_DE, nbrElem);
+
+ nbrElem = sizeof(specialInfoLine_RU) / sizeof(char *);
+ writeTextArray(outFile, specialInfoLine_RU, nbrElem);
+
+ nbrElem = sizeof(specialInfoLine_SP) / sizeof(char *);
+ writeTextArray(outFile, specialInfoLine_SP, nbrElem);
+
+// Not yet handled : miscTexts, endingLine and exitLine. Are they useful?
+
+ fclose(outFile);
+ return 0;
+}
+
+void writeTextArray(FILE *outFile, const char *textArray[], int nbrText) {
+ int len, len1, pad;
+ uint8 padBuf[DATAALIGNMENT];
+
+ for (int i = 0; i < DATAALIGNMENT; i++)
+ padBuf[i] = 0;
+
+ writeUint16BE(outFile, nbrText);
+ len = DATAALIGNMENT - 2;
+ for (int i = 0; i < nbrText; i++) {
+ len1 = strlen(textArray[i]) + 1;
+ pad = DATAALIGNMENT - (len1 + 2) % DATAALIGNMENT;
+ len += 2 + len1 + pad;
+ }
+ writeUint16BE(outFile, len);
+
+ fwrite(padBuf, DATAALIGNMENT - 2, 1, outFile); // padding
+ for (int i = 0; i < nbrText; i++) {
+ len = strlen(textArray[i]) + 1;
+ pad = DATAALIGNMENT - (len + 2) % DATAALIGNMENT;
+
+ writeUint16BE(outFile, len + pad + 2);
+ fwrite(textArray[i], len, 1, outFile);
+ fwrite(padBuf, pad, 1, outFile);
+ }
+}
+
diff --git a/tools/create_toon/create_toon.h b/tools/create_toon/create_toon.h
new file mode 100644
index 0000000000..0695944b17
--- /dev/null
+++ b/tools/create_toon/create_toon.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 CREATE_TOON_H
+#define CREATE_TOON_H
+
+#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+#define DATAALIGNMENT 4
+
+#define TOON_DAT_VER_MAJ 0 // 1 byte
+#define TOON_DAT_VER_MIN 3 // 1 byte
+
+// Number of Variante of the game. For the moment, it's the same
+// than the number of languages
+#define NUM_VARIANTE 5
+
+typedef unsigned char uint8;
+typedef unsigned char byte;
+typedef unsigned short uint16;
+typedef signed short int16;
+
+void writeTextArray(FILE *outFile, const char *textData[], int nbrText);
+
+#endif // CREATE_TOON_H
diff --git a/tools/create_toon/dists/msvc9/create_toon.sln b/tools/create_toon/dists/msvc9/create_toon.sln
new file mode 100644
index 0000000000..e9c3750590
--- /dev/null
+++ b/tools/create_toon/dists/msvc9/create_toon.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "create_toon", "create_toon.vcproj", "{5F280130-349D-11DD-AE16-0800200C9A66}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5F280130-349D-11DD-AE16-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5F280130-349D-11DD-AE16-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32
+ {5F280130-349D-11DD-AE16-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32
+ {5F280130-349D-11DD-AE16-0800200C9A66}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/tools/create_toon/dists/msvc9/create_toon.vcproj b/tools/create_toon/dists/msvc9/create_toon.vcproj
new file mode 100644
index 0000000000..f860b8b201
--- /dev/null
+++ b/tools/create_toon/dists/msvc9/create_toon.vcproj
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="create_toon"
+ ProjectGUID="{5F280130-349D-11DD-AE16-0800200C9A66}"
+ RootNamespace="create_toon"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4996"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\.."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/create_toon.exe"
+ LinkIncremental="2"
+ IgnoreDefaultLibraryNames="libc.lib;libcmt.lib"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/create_toon.pdb"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/wd4996"
+ Optimization="3"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ OutputFile="$(OutDir)/create_toon.exe"
+ LinkIncremental="1"
+ IgnoreDefaultLibraryNames="libc.lib;libcmt.lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\..\create_toon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\create_toon.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\staticdata.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/tools/create_toon/module.mk b/tools/create_toon/module.mk
new file mode 100644
index 0000000000..761afb7a7f
--- /dev/null
+++ b/tools/create_toon/module.mk
@@ -0,0 +1,10 @@
+MODULE := tools/create_toon
+
+MODULE_OBJS := \
+ create_toon.o
+
+# Set the name of the executable
+TOOL_EXECUTABLE := create_toon
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/tools/create_toon/staticdata.h b/tools/create_toon/staticdata.h
new file mode 100644
index 0000000000..efcb893024
--- /dev/null
+++ b/tools/create_toon/staticdata.h
@@ -0,0 +1,324 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along 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 STATICDATA_H
+#define STATICDATA_H
+
+const char *locationDirNotVisited_EN[] = {
+ "Lab", "Path", "Outside", "Pothole", "Palace Hallway",
+ "Door", "Secret Passage", "Doorway", "Doorway", "DUMMY",
+ "Path", "Shop", "Shop", "Shop", "Shop",
+ "Path", "Malevolated Countryside", "Malevolated Countryside", "Entrance", "Entrance",
+ "Path", "Path", "Central Zanydu", "Lower Zanydu", "Wacme Entrance",
+ "Upper Zanydu", "Entrance", "Entrance", "Path", "Path",
+ "Path", "Seedy's", "Entrance", "Entrance", "Path",
+ "DUMMY", "DUMMY", "Dungeon", "Hallway", "Air Vent",
+ "Upstairs", "Doorway", "Doorway", "Downstairs", "Rec Room",
+ "Doorway", "Secret Passageway", "Upstairs", "Upstairs", "Up",
+ "Doorway", "Upstairs", "Doorway", "Doorway", "Doorway",
+ "Swamp", "Forest", "Meadow", "Farmland", "Main Street",
+ "Costume Shop", "Shuttle Station", "Central Zanydu", "Upper North-Southeast Zanydu", "Lower South-Northwest Zanydu",
+ "Way-Outback", "High Road", "Frank's Lab", "Cryo-Crypt", "Fantasyworld",
+ "Vulture Falls", "Vulture Falls", "Vincent van Gogh's Studio", "Fearworld", "Attic",
+ "Unorthodontist's Office", "Bedroom", "The Malevolands", "Inhuman Robots, Inc.", "Inhuman Robots, Inc. Workshop",
+ "The End of the World", "Castle Ramparts", "Castle Top", "Kitchen", "Living Room",
+ "Bedroom", "Laboratory", "Storage Center First Floor", "Storage Center Second Floor", "Storage Center Third Floor",
+ "King Hugh's Palace", "Trophy Room", "Hallway", "Hallway", "Throne Room",
+ "Office", "Balcony", "Lighthouse", "Bedroom", "Hall of Reflection",
+ "Seedy's", "Barn", "WACME", "Lab"
+};
+
+const char *locationDirNotVisited_FR[] = {
+ "Labo", "Chemin", "Ext\351rieur", "Trou", "Hall du Palais",
+ "Porte", "Passage Secret", "Porte", "Porte", "DUMMY",
+ "Chemin", "Boutique", "Boutique", "Boutique", "Boutique",
+ "Chemin", "Champ perfidifi\351", "Champ perfidifi\351", "Entr\351e", "Entr\351e",
+ "Chemin", "Chemin", "Zanydu-Centre", "Bas de Zanydu", "Entr\351e des 3 Belges",
+ "Hauts de Zanydu", "Entr\351e", "Entr\351e", "Chemin", "Chemin",
+ "Chemin", "Bouling", "Entr\351e", "Entr\351e", "Chemin",
+ "DUMMY", "DUMMY", "Donjon", "Hall", "Conduit d'a\351ration",
+ "Etage sup\351rieur", "Porte", "Porte", "Etage inf\351rieur", "Salle de jeux",
+ "Porte", "Passage Secret", "Etage sup\351rieur", "Etage sup\351rieur", "Vers le haut",
+ "Porte", "Etage sup\351rieur", "Porte", "Porte", "Porte",
+ "Mar\351cage", "For\352t", "Prairie", "Champ", "Rue Principale",
+ "Boutique de costumes", "Gare Navette", "Zanydu-Centre", "Hauts de Zanydu Sud-Est du Nord", "Bas de Zanydu Nord-Ouest du Sud",
+ "Lointain lointain", "High Road", "Labo de Frank", "Cryo-Crypt", "Fantasyworld",
+ "Vulture Falls", "Vulture Falls", "Vincent van Gogh's Studio", "Fearworld", "Grenier",
+ "Unorthodontist's Office", "Chambre", "Perfidia", "Robots Inhumains SARL", "Robots Inhumains SARL. Atelier",
+ "La Fin du Monde", "Remparts du ch\342teau", "Castle Top", "Cuisine", "Salon",
+ "Chambre", "Laboratoire", "Storage Center First Floor", "Storage Center Second Floor", "Storage Center Third Floor",
+ "Ch\342teau de Hilarius 1er", "Salle des Troph\351es", "Hall", "Hall", "Salle du Tr\364ne",
+ "Bureau", "Balcon", "Lighthouse", "Chambre", "Hall of Reflection",
+ "Bouling", "Grange", "LES 3 BELGES", "Labo"
+};
+
+const char *locationDirNotVisited_DE[] = {
+ "Labor", "Weg", "Drau\337en", "Schlagloch", "Palastflur",
+ "T\374r", "Geheimgang", "T\374r", "T\374r", "DUMMY",
+ "Weg", "Laden", "Laden", "Laden", "Laden",
+ "Weg", "\334belierte Landschaft", "\334belierte Landschaft", "Eingang", "Eingang",
+ "Weg", "Weg", "Mittel-Trickreich", "Nieder-Trickreich", "Eingang der Fa. Wacme",
+ "Ober-Trickreich", "Eingang", "Eingang", "Weg", "Weg",
+ "Weg", "Seedys", "Eingang", "Eingang", "Weg",
+ "DUMMY", "DUMMY", "Kerker", "Flur", "Luftschacht",
+ "Nach oben", "T\374r", "T\374r", "Nach unten", "Ruheraum",
+ "T\374r", "Geheimgang", "Nach oben", "Nach oben", "Rauf",
+ "T\374r", "Nach oben", "T\374r", "T\374r", "T\374r",
+ "Sumpf", "Wald", "Wiese", "Ackerland", "Hauptstra\337e",
+ "Kost\374mverleih", "Bahnhaltestelle", "Mittel-Trickreich", "Ober-Nord-S\374dost-Trickreich", "Unteres S\374d-Nordwest-Trickreich",
+ "Weite W\374ste Wildnis", "Hochstra\337e", "Franks Labor", "K\344lteschlafkammer", "Phantasiawelt",
+ "Geierf\344lle", "Geierf\344lle", "Vincent van Goghs Studio", "Angstwelt", "Dachboden",
+ "Praxis des Unorthodontisten", "Schlafzimmer", "Das \334beland", "Unmenschliche Roboter AG", "Unmenschliche Roboter AG - Werkstatt",
+ "Das Ende der Welt", "Schlo\337w\344lle", "Oberer Teil des Schlosses", "K\374che", "Wohnzimmer",
+ "Schlafzimmer", "Labor", "Lager 1. Stock", "Lager 2. Stock", "Lager 3. Stock",
+ "K\366nig Nicks Palast", "Pokalsaal", "Flur", "Flur", "Thronsaal",
+ "B\374ro", "Balkon", "Leuchtturm", "Schlafzimmer", "Saal der Reflektion",
+ "Seedys", "Scheune", "WACME", "Labor"
+};
+
+const char *locationDirNotVisited_RU[] = {
+ "YBB", "Nelf", "Ds[jl", "Hsndbyf", "Dtcnb,.km",
+ "D[jl", "Nfqysq ghj[jl", "Ghj[jl", "Ghj[jl", "VFRTN",
+ "Nelf", "Ijg", "Ijg", "Ijg", "Ijg",
+ "Nelf", "Bcrjdthrfyyfz ptvkz", "Bcrjdthrfyyfz ptvkz", "D[jl", "D[jl",
+ "Nelf", "Nelf", "Wtynh", "Yb;yzz pjyf", "Gfhflysq d[jl",
+ "Dth[yzz pjyf", "D[jl", "D[jl", "Nelf", "Nelf",
+ "Nelf", "D[jl", "D[jl", "D[jl", "Nelf",
+ "VFRTN", "VFRTN", "Gjldfk", "Rjhbljh", "Kfp",
+ "Ddth[", "Ghj[jl", "Ghj[jl", "Dybp", "Buhjdfz",
+ "Ghj[jl", "Nfqysq ghj[jl", "Ddth[", "Ddth[", "!",
+ "Ghj[jl", "Ddth[", "Ghj[jl", "Ghj[jl", "Ghj[jl",
+ "Njgm", "Ktc", "Keu", "Athvf", "Ghjcgtrn",
+ "Jlt;lf", "Cnfywbz", "Wtynh", "Dth[ybq Ctdthj-.uj-djcnjr", "Yb;ybq >uj-ctdthj-pfgfl",
+ "Jrhfbyf", "Ljhjuf", "Kf,jhfnjhbz", "Crktg", "Vbh Afynfpbq",
+ "Cnthdjgfl", "Cnthdjgfl", "Cnelbz Dbyctynf dfy Ujuf", "Rjivfhbz", "Fnnbr",
+ "Rf,bytn Ytjhnjljrcf", "Cgfkmyz", "Pkjdtybz", "FJPN +Ytuevfyjbl+", "Vfcnthcrfz FJPN +Ytuevfyjbl+",
+ "Rhfq cdtnf", "Rhtgjcnyjq dfk", "<fiyz", "Re[yz", "Ujcnbyfz",
+ "Cgfkmyz", "Rf,bytn", "Gthdsq 'nf; [hfybkbof", "Dnjhjq 'nf; [hfybkbof", "Nhtnbq 'nf; [hfybkbof",
+ "Ldjhtw rjhjkz {m.", "Veptq", "Rjhbljh", "Rjhbljh", "Nhjyysq pfk",
+ "Jabc", "<fkrjy", "Vfzr", "Cgfkmyz", "Rjvyfnf hfplevbq",
+ "D[jl", "{ktd", "VJHLS", "YBB"
+};
+
+const char *locationDirNotVisited_SP[] = {
+ "Laboratorio", "Camino", "Exterior", "Bache", "Vest\355bulo del Palacio",
+ "Puerta", "Pasaje secreto", "Salida", "Salida", "Cosa extra\361a",
+ "Camino", "Tienda", "Tienda", "Tienda", "Tienda",
+ "Camino", "Paisaje malificado", "Paisaje malificado", "Entrada", "Entrada",
+ "Camino", "Camino", "Centro de Loquilandia", "Loquilandia de Abajo", "Entrada a Wacme",
+ "Loquilandia de arriba", "Entrada", "Entrada", "Camino", "Camino",
+ "Camino", "Sady's", "Entrada", "Entrada", "Camino",
+ "OBJETO EXTRA\321O", "OBJETO EXTRA\321O", "Mazmorra", "Vest\355bulo", "Conducto de la ventilaci\363n",
+ "Planta alta", "Salida", "Salida", "Planta baja", "Sala de entretenimiento",
+ "Salida", "Pasadizo secreto", "Planta alta", "Planta alta", "Arriba",
+ "Salida", "Planta alta", "Salida", "Salida", "Salida",
+ "Ci\351naga", "Bosque", "Pradera", "Tierras", "Calle principal",
+ "Tienda de disfraces", "Estaci\363n del telecabina", "Centro de Loquilandia", "Norsudeste de Loquilandia de Arriba", "Surnoroeste de Loquilandia de Abajo",
+ "Camino del interior", "Camino Real ", "Laboratorio de Frankestain", "Crio-Cripta", "Fantasilandia",
+ "Salto del buitre", "Salto del buitre", "Estudio de Vincent van Gogh ", "Miedilandia", "Atico",
+ "Despacho del Desortodoncista", "Dormitorio", "Malevolandia", "Robots Inhumanos, S.L.", "Robots Inhumanos, S.L. Taller",
+ "El fin del mundo", "Murallas del castillo", "Parte alta del castillo", "Cocina", "Sala de estar",
+ "Dormitorio", "Laboratorio", "Centro de almacenamiento del primer piso", "Centro de almacenamiento del segundo piso", "Centro de almacenamiento del tercer piso",
+ "Palacio del Rey Hugo", "Sala de trofeos", "Vest\355bulo", "Vest\355bulo", "Sal\363n del trono",
+ "Oficina", "Balc\363n", "Faro", "Dormitorio", "Sala de reflexi\363n",
+ "Sady's", "Establo", "Wacme", "Laboratorio"
+};
+
+const char *locationDirVisited_EN[] = {
+ "Lab", "Meadow", "King Hugh's Palace", "Pothole", "Palace Hallway",
+ "Bedroom", "Cellar", "Trophy Room", "Laboratory", "DUMMY",
+ "Town Center", "Bakery", "Costume Shop", "Tavern", "Arcade",
+ "Countryside", "Malevolated Countryside", "Malevolated Countryside", "Entrance", "Entrance",
+ "Forest", "Shuttle Station", "Central Zanydu", "Lower Zanydu", "Wacme Entrance",
+ "Upper Zanydu", "Entrance", "Entrance", "Way-Outback", "Wolf Den",
+ "The Malevolands", "Seedy's", "Robot Maker", "Prison", "The End of the World",
+ "DUMMY", "DUMMY", "Dungeon", "Climatron Room", "Air Vent",
+ "First Floor Landing", "Kitchen", "Padded Cell", "Second Floor Landing", "Wrecked Room",
+ "Knight Hall", "Surveillance Room", "Armory", "Third Floor Landing", "Bathroom",
+ "Study", "Stairwell", "Fourth Floor Landing", "Ms. Fortune's Lair", "Nefarious's Headquarters",
+ "Swamp", "Forest", "Meadow", "Farmland", "Main Street",
+ "Costume Shop", "Shuttle Station", "Inner-Central Middlemost Zanydu", "Upper North-Southeast Zanydu", "Lower South-Northwest Zanydu",
+ "Way-Outback", "High Road", "Frank's Lab", "Cryo-Crypt", "Fantasyworld",
+ "Vulture Falls", "Vulture Falls", "Vincent van Gogh's Studio", "Fearworld", "Attic",
+ "Unorthodontist's Office", "Bedroom", "The Malevolands", "Inhuman Robots, Inc.", "Inhuman Robots, Inc. Workshop",
+ "The End of the World", "Castle Ramparts", "Castle Top", "Kitchen", "Living Room",
+ "Bedroom", "Laboratory", "Storage Center First Floor", "Storage Center Second Floor", "Storage Center Third Floor",
+ "King Hugh's Palace", "Trophy Room", "Hallway", "Hallway", "Throne Room",
+ "Office", "Balcony", "Lighthouse", "Bedroom", "Hall of Reflection",
+ "Seedy's", "Barn", "WACME", "Lab"
+};
+
+const char *locationDirVisited_FR[] = {
+ "Labo", "Prairie", "Palais de Hilarius 1er", "Trou", "Hall du Palais",
+ "Chambre", "Cave", "Salle des Troph\351es", "Laboratoire", "DUMMY",
+ "Centre ville", "Boulangerie", "Boutique de costumes", "Taverne", "Salle d'arcade",
+ "Champ", "Champ perfidifi\351", "Champ perfidifi\351", "Etable", "Etable",
+ "For\352t", "Terminus Navette", "Zanydu-Centre", "Bas-Zanydu", "Entr\351e des 3 Belges",
+ "Hauts de Zanydu", "Entr\351e", "Entr\351e", "Lointain lointain", "Tani\350re des loups",
+ "Perfidia", "Bouling", "Faiseur de Robots", "Prison", "Le bout du monde",
+ "DUMMY", "DUMMY", "Donjon", "Salle du Climatron", "Conduit d'a\351ration",
+ "Palier 1er \351tage", "Cuisine", "Cellule capiton\351e", "Palier 2\350me \351tage", "Salle de jeux",
+ "Hall du Chevalier", "Salle de surveillance", "Armurerie", "Palier 3\350me \351tage", "Salle de bains",
+ "Bureau", "Escalier", "Palier 4\350me \351tage", "Antre de Miss Fortune", "Quartiers g\351n\351raux de N\351farius",
+ "Mar\351cage", "For\352t", "Prairie", "Champ", "Rue principale",
+ "Boutique de costumes", "Gare Navette", "Centre du coeur de Zanydu", "Haut-Zanydu Sud-Est du North", "Bas-Zanydu Nord-Ouest du Sud",
+ "Lointain lointain", "High Road", "Labo de Frank", "Cryo-Crypt", "Fantasyworld",
+ "Vulture Falls", "Vulture Falls", "Vincent van Gogh's Studio", "Fearworld", "Grenier",
+ "Unorthodontist's Office", "Chambre", "Perfidia", "Robots Inhumains SARL.", "Robots Inhumains SARL. Atelier",
+ "La Fin du Monde", "Remparts du Ch\342teau", "Castle Top", "Cuisine", "Salon",
+ "Chambre", "Laboratoire", "Storage Center First Floor", "Storage Center Second Floor", "Storage Center Third Floor",
+ "Palais d'Hilarius 1er", "Salle des Troph\351es", "Hall", "Hall", "Salle du Tr\364ne",
+ "Bureau", "Balcon", "Lighthouse", "Chambre", "Hall of Reflection",
+ "Bouling", "Grange", "LES 3 BELGES", "Labo"
+};
+
+const char *locationDirVisited_DE[] = {
+ "Labor", "Wiese", "K\366nig Nicks Palast", "Schlagloch", "Palastflur",
+ "Schlafzimmer", "Keller", "Pokalsaal", "Laboratorium", "DUMMY",
+ "Stadtmitte", "B\344ckerei", "Kost\374mverleih", "Kneipe", "Spielhalle",
+ "Landschaft", "\334belierte Landschaft", "\334belierte Landschaft", "Eingang", "Eingang",
+ "Wald", "Bahnhaltestelle", "Mittel-Trickreich", "Nieder-Trickreich", "Eingang der Fa. Wacme",
+ "Ober-Trickreich", "Eingang", "Eingang", "Weite W\374ste Wildnis", "Wolfsh\366hle",
+ "Das \334beland", "Seedys", "Roboterschmied", "Gef\344ngnis", "Das Ende der Welt",
+ "DUMMY", "DUMMY", "Verlies", "Klimatron-Zimmer", "Luftschacht",
+ "Treppenabsatz 1. Stock", "K\374che", "Gummizelle", "Treppenabsatz 2. Stock", "Demoliertes Zimmer",
+ "Rittersaal", "\334berwachungsraum", "Waffenkammer", "Treppenabsatz 3. Stock", "Badezimmer",
+ "Arbeitszimmer", "Treppenhaus", "Treppenabsatz 4. Stock", "Zimmer von Miss Gunst", "Hauptquartier von Widerlus",
+ "Sumpf", "Wald", "Wiese", "Ackerland", "Hauptstra\337e",
+ "Kost\374mverleih", "Bahnhaltestelle", "Zentrum des Inneren Mittel-Trickreichs", "Ober-Nord-S\374dost-Trickreich", "Unteres S\374d-Nordwest-Trickreich",
+ "Weite W\374ste Wildnis", "Hochstra\337e", "Franks Labor", "K\344lteschlafkammer", "Phantasiawelt",
+ "Geierf\344lle", "Geierf\344lle", "Vincent van Goghs Studio", "Angstwelt", "Dachboden",
+ "Praxis des Unorthodontisten", "Schlafzimmer", "Das \334beland", "Unmenschliche Roboter AG", "Unmenschliche Roboter AG - Werkstatt",
+ "Das Ende der Welt", "Schlo\337w\344lle", "Oberer Teil des Schlosses", "K\374che", "Wohnzimmer",
+ "Schlafzimmer", "Labor", "Lager 1. Stock", "Lager 2. Stock", "Lager 3. Stock",
+ "K\366nig Nicks Palast", "Pokalsaal", "Flur", "Flur", "Thronsaal",
+ "B\374ro", "Balkon", "Leuchtturm", "Schlafzimmer", "Saal der Reflektion",
+ "Seedys", "Scheune", "WACME", "Labor"
+};
+
+const char *locationDirVisited_RU[] = {
+ "YBB", "Keu", "Ldjhtw rjhjkf {m.", "Hsndbyf", "Dtcnb,.km",
+ "Cgfkmyz", "Gjuht,", "Veptq", "Rf,bytn", "VFRTN",
+ "Wtynh", "{kt,", "Jlt;lf", "Rf,fr", "Buhs",
+ "Lthtdyz", "Bcrjdthrfyyfz ptvkz", "Bcrjdthrfyyfz ptvkz", "D[jl", "D[jl",
+ "Ktc", "Cnfywbz", "Wtynh", "Yb;yzz pjyf", "Gfhflysq d[jl",
+ "Dth[yzz pjyf", "D[jl", "D[jl", "Jrhfbyf", "Kjujdj",
+ "Pkjdtybz", "D[jl", "Hj,jn", "N.hmvf", "Rhfq cdtnf",
+ "VFRTN", "VFRTN", "Gjldfk", "Rkbvfnhjyyfz", "Kfp",
+ "Gkjoflrf 1-uj 'nf;f", "Re[yz", "Rfvthf", "Gkjoflrf 2-uj 'nf;f", "Fynbrdfhbfn",
+ "Hswfhcrfz", "Rjvyfnf j[hfys", "Jhe;bt", "Gkjoflrf 3-uj 'nf;f", "Dfyyfz",
+ "Jabc", "Rjkjltw", "Gkjoflrf 4-uj 'nf;f", "Yjhf vbcc Ajhneys", "Inf,-rdfhnbhf Ytafhbecf",
+ "Njgm", "Ktc", "Keu", "Athvf", "Ghjcgtrn",
+ "Jlt;lf", "Cnfywbz", "Chtlyzz pjyf dyenhtyytuj wtynhf", "Yb;ybq Ctdthj-.uj-djcnjr", "Yb;ybq >uj-ctdthj-pfgfl",
+ "Jrhfbyf", "Ljhjuf", "Kf,jhfnjhbz", "Crktg", "Vbh Afynfpbq",
+ "Cnthdjgfl", "Cnthdjgfl", "Cnelbz Dbyctynf dfy Ujuf", "Rjivfhbz", "Fnnbr",
+ "Rf,bytn Ytjhnjljrcf", "Cgfkmyz", "Pkjdtybz", "FJPN +Ytuevfyjbl+", "Vfcnthcrfz FJPN +Ytuevfyjbl+",
+ "Rhfq cdtnf", "Rhtgjcnyjq dfk", "<fiyz", "Re[yz", "Ujcnbyfz",
+ "Cgfkmyz", "Rf,bytn", "Gthdsq 'nf; [hfybkbof", "Dnjhjq 'nf; [hfybkbof", "Nhtnbq 'nf; [hfybkbof",
+ "Ldjhtw rjhjkz {m.", "Veptq", "Rjhbljh", "Rjhbljh", "Nhjyysq pfk",
+ "Jabc", "<fkrjy", "Vfzr", "Cgfkmyz", "Rjvyfnf hfplevbq",
+ "D[jl", "{ktd", "VJHLS", "YBB"
+};
+
+const char *locationDirVisited_SP[] = {
+ "Laboratorio", "Pradera", "Palacio del Rey Hugo", "Bache", "Vest\355bulo del palacio",
+ "Dormitorio", "S\363tano", "Sala de trofeos", "Laboratorio", "Mu\361eco",
+ "Centro ciudad", "Panader\355a", "Tienda de disfraces", "Taberna", "Sal\363n de juegos recreativos",
+ "Paisaje", "Paisaje malificado", "Paisaje malificado", "Entrada", "Entrada",
+ "Bosque", "Estaci\363n del telecabina", "Centro de Loquilandia", "Loquilandia de Abajo", "Entrada a Wacme",
+ "Loquilandia de Arriba", "Entrada", "Entrada", "Camino del interior", "Guarida de lobos",
+ "Malevolandia", "Sady's", "Fabricante de robots", "Prisi\363n", "El fin del mundo",
+ "Cosa extra\361a", "Cosa extra\361a", "Mazmorra", "Sala del Climatr\363n", "Conducto de ventilaci\363n",
+ "Descansillo del primer piso", "Cocina", "Jaula de locos", "Descansillo del segundo piso", "Cuarto destrozado",
+ "Sala del caballero", "Sala de vigilancia", "Sala de blasones", "Descansillo del tercer piso", "Cuarto de ba\361o",
+ "Estudio", "Escalera", "Descansillo del cuarto piso", "Guarida de Lady Infortunata", "Dependencias de Nefastus",
+ "Ci\351naga", "Bosque", "Pradera", "Tierras", "Calle principal",
+ "Tienda de disfraces", "Estaci\363n del telecabina", "Casi medio centro de Loquilandia", "Norsudeste de Loquilandia de Arriba", "Surnoroeste de Loquilandia de Abajo",
+ "Camino del interior", "Camino Real", "Laboratorio de Frankestain", "Crio-Cripta", "Fantasilandia",
+ "Salto del buitre", "Salto del buitre", "Estudio de Vincent van Gogh ", "Miedilandia", "Atico",
+ "Despacho del Desortodoncista", "Dormitorio", "Malevolandia", "Robots Inhumanos, S.L.", "Robots Inhumanos, S.L. Taller",
+ "El fin del mundo", "Murallas del castillo", "Parte alta del castillo", "Cocina", "Sala de estar",
+ "Dormitorio", "Laboratorio", "Centro de almacenamiento del primer piso", "Centro de almacenamiento del segundo piso", "Centro de almacenamiento del tercer piso",
+ "Palacio del Rey Hugo", "Sala de trofeos", "Vest\355bulo", "Vest\355bulo", "Sal\363n del trono",
+ "Oficina", "Balc\363n", "Faro", "Dormitorio", "Sala de reflexi\363n",
+ "Sady's", "Establo", "Wacme", "Laboratorio "
+};
+
+const char *specialInfoLine_EN[] = { "Exit not defined", "Bottomless Bag", "Flux Wildly", "Drew Blanc" };
+const char *specialInfoLine_FR[] = { "Exit not defined", "Inventaire", "Flux Radieux", "Marc Blanc" };
+const char *specialInfoLine_DE[] = { "Exit not defined", "Bodenloser Beutel", "Flux W. Wild", "Mal Block" };
+const char *specialInfoLine_RU[] = { "Exit not defined", " Fdjcmrf ", " :bdxbr ", "Lhe <k'yr " };
+const char *specialInfoLine_SP[] = { "Exit not defined", "Saco sin fondo", "Flux Tarambana", "Andr\351s Truido" };
+
+// Those are not yet in the DAT file. They'll be added as soon as they are really used.
+const char *miscTexts_EN[] = {
+ "Are you sure you want to exit? (Y/N)",
+ "Please insert Disc One",
+ "Please insert Disc Two",
+ "File %s is missing. Press a key."
+};
+
+const char *miscTexts_FR[] = {
+ "Etes-vous s\373r(e) de vouloir quitter ? (O/N)",
+ "Ins\351rez le CD 1",
+ "Ins\351rez le CD 2",
+ "Le fichier %s est manquant. Appuyez sur une touche."
+};
+
+const char *miscTexts_DE[] = {
+ "Aufh\366ren? Sind Sie sicher? (J/N)",
+ "Bitte CD 1 einlegen",
+ "Bitte CD 2 einlegen",
+ "Datei %s fehlt. Beliebige Taste dr\374cken."
+};
+
+const char *miscTexts_RU[] = {
+ "Are you sure you want to exit? (Y/N)",
+ "Please insert Disc One",
+ "Please insert Disc Two",
+ "File %s is missing. Press a key."
+};
+
+const char *miscTexts_SP[] = {
+ "\277Est\341s seguro de que quieres salir? (S/N)",
+ "Por favor, inserta el disco 1",
+ "Por favor, inserta el disco 2",
+ "El archivo %s no aparece. Pulsa una tecla."
+};
+
+const char *endingLine_EN = "Congratulations!!! Hope you enjoyed playing ToonStruck!!";
+const char *endingLine_FR = "F\202licitations ! Nous esp\202rons que vous avez aim\202 ToonStruck !";
+const char *endingLine_DE = "Herzlichen Gl\201ckwunsch! Wir hoffen, Toonstruck hat Ihnen Spa\341 gemacht!";
+const char *endingLine_RU = "Congratulations!!! Hope you enjoyed playing ToonStruck!!";
+const char *endingLine_SP = "\255\255Enhorabuena!! \255\255Esperamos que te diviertas jugando a ToonStruck!!";
+
+const char *exitLine_EN = "Hope you enjoyed playing ToonStruck!!";
+const char *exitLine_FR = "Nous esp\202rons que vous avez aim\202 jouer \205 ToonStruck !";
+const char *exitLine_DE = "Wir hoffen, Toonstruck hat Ihnen Spa\341 gemacht!";
+const char *exitLine_RU = "Hope you enjoyed playing ToonStruck!!";
+const char* exitLine_SP = "\255\255Esperamos que te diviertas jugando a ToonStruck!!";
+
+#endif
+
diff --git a/tools/create_translations/create_translations.cpp b/tools/create_translations/create_translations.cpp
index fab35cdfd5..9fcf3b4a31 100644
--- a/tools/create_translations/create_translations.cpp
+++ b/tools/create_translations/create_translations.cpp
@@ -52,7 +52,7 @@ void writeUint16BE(FILE *fp, uint16 value) {
writeByte(fp, (uint8)(value & 0xFF));
}
-int stringSize(const char* string) {
+int stringSize(const char *string) {
// Each string is preceded by its size coded on 2 bytes
if (string == NULL)
return 2;
@@ -63,7 +63,7 @@ int stringSize(const char* string) {
// return 2 + len + pad;
}
-void writeString(FILE *fp, const char* string) {
+void writeString(FILE *fp, const char *string) {
// Each string is preceded by its size coded on 2 bytes
if (string == NULL) {
writeUint16BE(fp, 0);
@@ -86,12 +86,12 @@ int main(int argc, char *argv[]) {
PoMessageList messageIds;
PoMessageEntryList **translations = new PoMessageEntryList*[argc - 1];
int numLangs = 0;
- for (int i = 1 ; i < argc ; ++i) {
+ for (int i = 1; i < argc; ++i) {
translations[numLangs] = parsePoFile(argv[i], messageIds);
if (translations[numLangs] != NULL)
++numLangs;
}
-
+
FILE *outFile;
int i, lang;
int len;
@@ -110,7 +110,7 @@ int main(int argc, char *argv[]) {
// Write number of translations
writeUint16BE(outFile, numLangs);
-
+
// Write the length of each data block here.
// We could write it at the start of each block but that would mean that
// to go to block 4 we would have to go at the start of each preceding block,
@@ -123,7 +123,7 @@ int main(int argc, char *argv[]) {
// 3. First translation
// 4. Second translation
// ...
-
+
// Write length for translation description
len = 0;
for (lang = 0; lang < numLangs; lang++) {
@@ -131,7 +131,7 @@ int main(int argc, char *argv[]) {
len += stringSize(translations[lang]->languageName());
}
writeUint16BE(outFile, len);
-
+
// Write size for the original language (english) block
// It starts with the number of strings coded on 2 bytes followed by each
// string (two bytes for the number of chars and the string itself).
@@ -158,13 +158,13 @@ int main(int argc, char *argv[]) {
writeString(outFile, translations[lang]->language());
writeString(outFile, translations[lang]->languageName());
}
-
+
// Write original messages
writeUint16BE(outFile, messageIds.size());
for (i = 0; i < messageIds.size(); ++i) {
writeString(outFile, messageIds[i]);
}
-
+
// Write translations
for (lang = 0; lang < numLangs; lang++) {
writeUint16BE(outFile, translations[lang]->size());
@@ -177,11 +177,11 @@ int main(int argc, char *argv[]) {
}
fclose(outFile);
-
+
// Clean the memory
for (i = 0; i < numLangs; ++i)
delete translations[i];
- delete [] translations;
+ delete[] translations;
return 0;
}
diff --git a/tools/create_translations/po_parser.cpp b/tools/create_translations/po_parser.cpp
index 3d8e2547a0..bc49da40d4 100644
--- a/tools/create_translations/po_parser.cpp
+++ b/tools/create_translations/po_parser.cpp
@@ -34,14 +34,14 @@ PoMessageList::PoMessageList() : _messages(NULL), _size(0), _allocated(0) {
PoMessageList::~PoMessageList() {
for (int i = 0; i < _size; ++i)
- delete [] _messages[i];
- delete [] _messages;
+ delete[] _messages[i];
+ delete[] _messages;
}
void PoMessageList::insert(const char *msg) {
if (msg == NULL || *msg == '\0')
return;
-
+
// binary-search for the insertion index
int leftIndex = 0;
int rightIndex = _size - 1;
@@ -64,7 +64,7 @@ void PoMessageList::insert(const char *msg) {
newMessages[i] = _messages[i];
for (int i = leftIndex; i < _size; ++i)
newMessages[i + 1] = _messages[i];
- delete [] _messages;
+ delete[] _messages;
_messages = newMessages;
} else {
for (int i = _size - 1; i >= leftIndex; --i)
@@ -78,11 +78,11 @@ void PoMessageList::insert(const char *msg) {
int PoMessageList::findIndex(const char *msg) {
if (msg == NULL || *msg == '\0')
return -1;
-
+
// binary-search for the message
int leftIndex = 0;
int rightIndex = _size - 1;
-
+
while (rightIndex >= leftIndex) {
const int midIndex = (leftIndex + rightIndex) / 2;
const int compareResult = strcmp(msg, _messages[midIndex]);
@@ -122,12 +122,12 @@ PoMessageEntryList::PoMessageEntryList(const char *lang) :
}
PoMessageEntryList::~PoMessageEntryList() {
- delete [] _lang;
- delete [] _charset;
- delete [] _langName;
+ delete[] _lang;
+ delete[] _charset;
+ delete[] _langName;
for (int i = 0; i < _size; ++i)
delete _list[i];
- delete [] _list;
+ delete[] _list;
}
void PoMessageEntryList::addMessageEntry(const char *translation, const char *message, const char *context) {
@@ -136,17 +136,17 @@ void PoMessageEntryList::addMessageEntry(const char *translation, const char *me
// We get the charset and the language name from the translation string
char *str = parseLine(translation, "Language:");
if (str != NULL) {
- delete [] _langName;
+ delete[] _langName;
_langName = str;
}
str = parseLine(translation, "charset=");
if (str != NULL) {
- delete [] _charset;
+ delete[] _charset;
_charset = str;
}
return;
- }
-
+ }
+
// binary-search for the insertion index
int leftIndex = 0;
int rightIndex = _size - 1;
@@ -189,8 +189,8 @@ void PoMessageEntryList::addMessageEntry(const char *translation, const char *me
if (contextIndex < leftIndex && _list[contextIndex]->msgctxt == NULL && strcmp(translation, _list[contextIndex]->msgstr) == 0)
return;
}
-
-
+
+
if (_size + 1 > _allocated) {
_allocated += 100;
PoMessageEntry **newList = new PoMessageEntry*[_allocated];
@@ -198,7 +198,7 @@ void PoMessageEntryList::addMessageEntry(const char *translation, const char *me
newList[i] = _list[i];
for (int i = leftIndex; i < _size; ++i)
newList[i + 1] = _list[i];
- delete [] _list;
+ delete[] _list;
_list = newList;
} else {
for (int i = _size - 1; i >= leftIndex; --i)
@@ -206,7 +206,7 @@ void PoMessageEntryList::addMessageEntry(const char *translation, const char *me
}
_list[leftIndex] = new PoMessageEntry(translation, message, context);
++_size;
-
+
if (context == NULL || *context == '\0') {
// Remove identical translations for a specific context (see comment above)
int contextIndex = leftIndex + 1;
@@ -228,7 +228,7 @@ void PoMessageEntryList::addMessageEntry(const char *translation, const char *me
}
_size -= removed;
}
-
+
}
const char *PoMessageEntryList::language() const {
@@ -258,10 +258,10 @@ PoMessageEntryList *parsePoFile(const char *file, PoMessageList& messages) {
FILE *inFile = fopen(file, "r");
if (!inFile)
return NULL;
-
+
char msgidBuf[1024], msgctxtBuf[1024], msgstrBuf[1024];
- char line[1024], *currentBuf = NULL;
-
+ char line[1024], *currentBuf = msgstrBuf;
+
// Get language from file name and create PoMessageEntryList
int index = 0, start_index = strlen(file) - 1;
while (start_index > 0 && file[start_index - 1] != '/' && file[start_index - 1] != '\\') {
@@ -273,34 +273,53 @@ PoMessageEntryList *parsePoFile(const char *file, PoMessageList& messages) {
}
msgidBuf[index] = '\0';
PoMessageEntryList *list = new PoMessageEntryList(msgidBuf);
-
+
+ // Initialize the message attributes.
+ bool fuzzy = false;
+ bool fuzzy_next = false;
+
// Parse the file line by line.
// The msgstr is always the last line of an entry (i.e. msgid and msgctxt always
// precede the corresponding msgstr).
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
while (!feof(inFile) && fgets(line, 1024, inFile)) {
+ if (line[0] == '#' && line[1] == ',') {
+ // Handle message attributes.
+ if (strstr(line, "fuzzy")) {
+ fuzzy_next = true;
+ continue;
+ }
+ }
// Skip empty and comment line
if (*line == '\n' || *line == '#')
continue;
if (strncmp(line, "msgid", 5) == 0) {
if (currentBuf == msgstrBuf) {
// add previous entry
- if (*msgstrBuf != '\0') {
+ if (*msgstrBuf != '\0' && !fuzzy) {
messages.insert(msgidBuf);
list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf);
}
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
+
+ // Reset the attribute flags.
+ fuzzy = fuzzy_next;
+ fuzzy_next = false;
}
strcpy(msgidBuf, stripLine(line));
currentBuf = msgidBuf;
} else if (strncmp(line, "msgctxt", 7) == 0) {
if (currentBuf == msgstrBuf) {
// add previous entry
- if (*msgstrBuf != '\0') {
+ if (*msgstrBuf != '\0' && !fuzzy) {
messages.insert(msgidBuf);
list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf);
}
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
+
+ // Reset the attribute flags
+ fuzzy = fuzzy_next;
+ fuzzy_next = false;
}
strcpy(msgctxtBuf, stripLine(line));
currentBuf = msgctxtBuf;
@@ -313,7 +332,7 @@ PoMessageEntryList *parsePoFile(const char *file, PoMessageList& messages) {
strcat(currentBuf, stripLine(line));
}
}
-
+
fclose(inFile);
return list;
}
@@ -321,15 +340,42 @@ PoMessageEntryList *parsePoFile(const char *file, PoMessageList& messages) {
char *stripLine(char *line) {
// This function modifies line in place and return it.
// Keep only the text between the first two unprotected quotes.
+ // It also look for literal special characters (e.g. preceded by '\n', '\\', '\"', '\'', '\t')
+ // and replace them by the special character so that strcmp() can match them at run time.
// Look for the first quote
int start = 0;
int len = strlen(line);
while (start < len && line[start++] != '"') {}
// shift characters until we reach the end of the string or an unprotected quote
- int i = 0;
- while (start+i < len && (line[start+i] != '"' || (i > 0 && line[start+i-1] == '\\'))) {
- line[i] = line[start+i];
- ++i;
+ int i = 0, j = 0;
+ while (start + i + j < len && line[start + i + j] != '"') {
+ if (line[start + i + j] == '\\') {
+ switch (line[start + i + j + 1]) {
+ case 'n':
+ line[i++] = '\n';
+ break;
+ case 't':
+ line[i++] = '\t';
+ break;
+ case '\"':
+ line[i++] = '\"';
+ break;
+ case '\'':
+ line[i++] = '\'';
+ break;
+ case '\\':
+ line[i++] = '\\';
+ break;
+ default:
+ // Just skip
+ fprintf(stdout, "Unsupported special character \"%c%c\" in string. Please contact ScummVM developers.\n", line[start + i + j], line[start + i + j + 1]);
+ ++j;
+ }
+ ++j;
+ } else {
+ line[i] = line[start + i + j];
+ ++i;
+ }
}
line[i] = '\0';
return line;
@@ -348,10 +394,9 @@ char *parseLine(const char *line, const char *field) {
while (*str != '\0' && isspace(*str)) {
++str;
}
- // Find string length (top at the first '\\'
- // (since the string we want is followed by a '\\n')
+ // Find string length (stop at the first '\n')
int len = 0;
- while (str[len] != '\0' && str[len] != '\\') {
+ while (str[len] != '\0' && str[len] != '\n') {
++len;
}
if (len == 0)
diff --git a/tools/create_translations/po_parser.h b/tools/create_translations/po_parser.h
index ac54a7fdae..6991b1d11e 100644
--- a/tools/create_translations/po_parser.h
+++ b/tools/create_translations/po_parser.h
@@ -34,7 +34,7 @@ public:
void insert(const char *msg);
int findIndex(const char *msg);
-
+
int size() const;
const char *operator[](int) const;
@@ -69,9 +69,9 @@ struct PoMessageEntry {
}
}
~PoMessageEntry() {
- delete [] msgstr;
- delete [] msgid;
- delete [] msgctxt;
+ delete[] msgstr;
+ delete[] msgid;
+ delete[] msgctxt;
}
};
@@ -80,11 +80,11 @@ struct PoMessageEntry {
*/
class PoMessageEntryList {
public:
- PoMessageEntryList(const char* language);
+ PoMessageEntryList(const char *language);
~PoMessageEntryList();
void addMessageEntry(const char *translation, const char *message, const char *context = NULL);
-
+
const char *language() const;
const char *languageName() const;
const char *charset() const;
diff --git a/tools/credits.pl b/tools/credits.pl
index f59c68021a..67d134d1d5 100755
--- a/tools/credits.pl
+++ b/tools/credits.pl
@@ -542,6 +542,14 @@ begin_credits("Credits");
add_person("Jonathan Gray", "khalek", "(retired)");
end_section();
+ begin_section("Broken Sword 2.5");
+ add_person("Eugene Sandulenko", "sev", "");
+ add_person("Filippos Karapetis", "[md5]", "");
+ add_person("Max Horn", "Fingolfin", "");
+ add_person("Paul Gilbert", "dreammaster", "");
+ add_person("Torbj&ouml;rn Andersson", "eriktorbjorn", "");
+ end_section();
+
begin_section("Cinematique evo 1");
add_person("Vincent Hamm", "yaz0r", "(retired)");
add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
@@ -584,6 +592,12 @@ begin_credits("Credits");
add_person("Jordi Vilalta Prat", "jvprat", "");
end_section();
+ begin_section("Hugo");
+ add_person("Arnaud Boutonn&eacute;", "Strangerke", "");
+ add_person("Oystein Eftevaag", "vinterstum", "");
+ add_person("Eugene Sandulenko", "sev", "");
+ end_section();
+
begin_section("Kyra");
add_person("Torbj&ouml;rn Andersson", "eriktorbjorn", "VQA Player");
add_person("Oystein Eftevaag", "vinterstum", "");
@@ -592,6 +606,12 @@ begin_credits("Credits");
add_person("Johannes Schickel", "LordHoto", "");
end_section();
+ begin_section("Last Express");
+ add_person("Matthew Hoops", "clone2727", "");
+ add_person("Jordi Vilalta Prat", "jvprat", "");
+ add_person("Julien Templier", "littleboy", "");
+ end_section();
+
begin_section("Lure");
add_person("Paul Gilbert", "dreammaster", "");
end_section();
@@ -602,7 +622,7 @@ begin_credits("Credits");
add_person("Benjamin Haisch", "john_doe", "");
add_person("Filippos Karapetis", "[md5]", "");
end_section();
-
+
begin_section("MADE");
add_person("Benjamin Haisch", "john_doe", "");
add_person("Filippos Karapetis", "[md5]", "");
@@ -614,7 +634,7 @@ begin_credits("Credits");
add_person("Eugene Sandulenko", "sev", "");
add_person("David Turner", "digitall", "");
end_section();
-
+
begin_section("Parallaction");
add_person("", "peres", "");
end_section();
@@ -654,6 +674,10 @@ begin_credits("Credits");
add_person("Joost Peters", "joostp", "");
end_section();
+ begin_section("Toon");
+ add_person("Sylvain Dupont", "SylvainTV", "");
+ end_section();
+
begin_section("Touch&eacute;");
add_person("Gregory Montoir", "cyx", "");
end_section();
@@ -674,7 +698,7 @@ begin_credits("Credits");
add_person("Marcus Comstedt", "", "");
end_section();
- begin_section("GP2X");
+ begin_section("GPH Devices (GP2X, GP2XWiz &amp; Caanoo)");
add_person("John Willis", "DJWillis", "");
end_section();
@@ -698,6 +722,10 @@ begin_credits("Credits");
add_person("Neil Millstone", "agent-q", "");
end_section();
+ begin_section("OpenPandora");
+ add_person("John Willis", "DJWillis", "");
+ end_section();
+
begin_section("PocketPC / WinCE");
add_person("Nicolas Bacca", "arisme", "(retired)");
add_person("Kostas Nakos", "Jubanka", "");
@@ -851,7 +879,7 @@ begin_credits("Credits");
add_person("Johannes Schickel", "LordHoto", "");
end_section();
end_section();
-
+
begin_section("Translations");
begin_persons();
add_person("Thierry Crozat", "criezy", "Translation Lead");
@@ -982,6 +1010,7 @@ begin_credits("Credits");
add_person("Curt Coder", "", "For the original TrollVM (preAGI) code");
add_person("Patrick Combet", "Dorian Gray", "For the original Gobliiins ADL player");
add_person("Ivan Dubrov", "", "For contributing the initial version of the Gobliiins engine");
+ add_person("Henrik Engqvist", "qvist", "For generously providing hosting for our buildbot, SVN repository, planet and doxygen sites as well as tons of HD space");
add_person("DOSBox Team", "", "For their awesome OPL2 and OPL3 emulator");
add_person("Till Kresslein", "Krest", "For design of modern ScummVM GUI");
add_person("", "Jezar", "For his freeverb filter implementation");
@@ -1023,6 +1052,19 @@ begin_credits("Credits");
"John Young, Colin Smythe and especially Terry Pratchett himself for ".
"sharing the source code of Discworld I &amp; II with us.");
+ add_paragraph(
+ "Emilio de Paz Arag&oacute;n from Alcachofa Soft for sharing the source code ".
+ "of Drascula: The Vampire Strikes Back with us and his generosity with ".
+ "freewaring the game.");
+
+ add_paragraph(
+ "David P. Gray from Gray Design Associate for sharing the source code ".
+ "of the Hugo trilogy.");
+
+ add_paragraph(
+ "Broken Sword 2.5 team for providing sources of their engine and their great ".
+ "support.");
+
end_section();
end_credits();
diff --git a/tools/sci/classes.cpp b/tools/sci/classes.cpp
deleted file mode 100644
index d7699bfe4c..0000000000
--- a/tools/sci/classes.cpp
+++ /dev/null
@@ -1,76 +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 <stdio.h>
-#include <stdlib.h>
-
-#include <engine.h>
-#include <vocabulary.h>
-
-int main(int argc, char** argv) {
- int res;
- int sizes[1000];
- int altsizes[1000];
- int count, *classes;
- loadResources(SCI_VERSION_AUTODETECT, 1);
-
- for (res = 0; res < 1000; res++) {
- resource_t* r;
- int i = 0;
-
- sizes[res] = -1;
-
- if ((r = findResource(sci_script, res)) == 0) continue;
- sizes[res] = 0;
- altsizes[res] = 0;
- i += 2;
- i = getInt16(r->data + i);
-
- while (i < r->length - 2) {
- switch (getInt16(r->data + i)) {
- case 1:
- case 6:
- sizes[res]++;
- break;
- default:
- altsizes[res]++;
- }
- i += getInt16(r->data + i + 2);
- }
- fflush(stdout);
- }
-
- for (res = 0; res < 1000; res++) if (sizes[res] != -1) printf("%03d %d\n", res, sizes[res]);
- printf("\n");
-
- classes = vocabulary_get_classes(&count);
- for (res = 0; res < count; res++) {
- printf("%03d %d (%d)\n", classes[res],
- sizes[classes[res]]--, altsizes[classes[res]]);
- }
-
- freeResources();
- return 0;
-}
diff --git a/tools/sci/graphics_png.h b/tools/sci/graphics_png.h
deleted file mode 100644
index 84358478f1..0000000000
--- a/tools/sci/graphics_png.h
+++ /dev/null
@@ -1,112 +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$
- *
- */
-
-/* Provides facilities for writing pictures and seperate views to .png files */
-
-
-#ifndef _SCI_GRAPHICS_PNG_H_
-#define _SCI_GRAPHICS_PNG_H_
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef HAVE_LIBPNG
-
-#include <resource.h>
-#include <graphics.h>
-#include <png.h>
-
-int
-write_pic_png(char *filename, guint8 *pic);
-/* Stores a picture map in a png file
-** --DEPRECATED--
-** Parameters: (filename) The name of the file to create
-** (pic) A pointer to the picture map to store
-** Returns : (int) 0 on success, != 0 otherwise
-** Please note that pic must not point to a picture_t, it must point to a
-** map.
-** E.g. to store the foreground picture, you could use
-** write_pic_png("filename.png", picture->maps[0]).
-*/
-
-
-int
-png_save_buffer(picture_t pic, char *name,
- int xoffset, int yoffset, int width, int height,
- byte *data, int force_8bpp_special);
-/* Stores any buffer in a png file
-** Parameters: (picture_t) pic: The picture_t containing the parameters of the buffer
-** (char *) name: File name to write to
-** (int x int) xoffset, yoffset: relative screen position of the buffer
-** (int x int) width, height: Buffer size
-** (byte *) data: The actual data to write
-** (int) force_8bpp_special: See below
-** Returns : (int) 0 on success, 1 otherwise
-** Each graphics buffer is stored in the way that the corresponding picture_t
-** dictates (all buffers are either identical to, or parts of a past representation
-** of the picture_t map 0, after all).
-** force_8bpp_special overrides this and forces 8bpp palette mode for writing the
-** buffer.
-** This should only be used for maps 1 through 3, which are always in this format.
-*/
-
-
-byte *
-png_load_buffer(picture_t pic, char *name,
- int *xoffset, int *yoffset, int *width, int *height,
- int *size, int force_8bpp_special);
-/* Loads a buffer from a png file
-** Parameters: (picture_t) pic: The picture_t containing the target parameters
-** (char *) name: The file name of the file to read from
-** (int* x int*) xoffset, yoffset: Offset storage pointers
-** (int* x int*) width, height: Size storage pointers
-** (int*) size: Memory size storage pointer
-** (int) force_8bpp_special: Import file as 8bpp with palette
-** Returns : (byte *): The buffer on success, NULL otherwise
-** xoffset, yoffset, width, height, and size should each point to ints the picture
-** parameters are stored in. xoffset and yoffset may be set to NULL, though, in
-** order to be omitted.
-*/
-
-
-int
-png_save_pic(picture_t pic);
-/* Stores a picture_t in the current directory
-** Parameters: (picture_t) pic: The picture_t to store
-** Returns : (int) 0 on success, 1 otherwise
-*/
-
-int
-png_load_pic(picture_t pic);
-/* Loads a picture_t from the cwd
-** Parameters: (picture_t) pic: The structure to write to
-** Returns : (int) 0 on success, 1 otherwise
-** On error, the original pic status is retained.
-*/
-
-
-#endif /* HAVE_LIBPNG */
-#endif /* !_SCI_GRAPHICS_PNG_H_ */
diff --git a/tools/sci/listwords.cpp b/tools/sci/listwords.cpp
deleted file mode 100644
index 3951a1e779..0000000000
--- a/tools/sci/listwords.cpp
+++ /dev/null
@@ -1,85 +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 "sciunpack.h"
-#include <engine.h>
-#include <kernel.h>
-
-int
-_vocab_cmp_group(const void *word1, const void *word2) {
-#define fw (* ((word_t **) word1))
-#define sw (* ((word_t **) word2))
- if (fw->group < sw->group)
- return -1;
- else if (fw->group == sw->group)
- return 0;
- else
- return 1;
-}
-
-int vocab_sort = DEFAULT_SORTING;
-
-int
-vocab_print(void) {
- int b, words_nr, counter;
- word_t **words, **tracker;
-
- tracker = words = vocab_get_words(resmgr, &words_nr);
-
- counter = words_nr;
-
- if (vocab_sort == SORT_METHOD_GROUP)
- qsort(words, words_nr, sizeof(word_t *), _vocab_cmp_group); /* Sort entries */
-
- while (counter--) {
- printf("%s (class %03x, group %03x) ", &tracker[0]->word,
- tracker[0]->w_class, tracker[0]->group);
-
- if ((tracker[0]->w_class >= 0xf00) ||
- (tracker[0]->w_class == 0))
- printf("anyword\n");
- else
- while (tracker[0]->w_class) {
- b = sci_ffs(tracker[0]->w_class) - 1;
- tracker[0]->w_class &= ~(1 << b);
- printf("%s", class_names[b]);
- if (tracker[0]->w_class)
- printf("|");
- else
- printf("\n");
- }
- tracker++;
- }
-
- vocab_free_words(words, words_nr);
-
- return 0;
-}
-
-
-
-
-
-
diff --git a/tools/sci/old_objects.cpp b/tools/sci/old_objects.cpp
deleted file mode 100644
index 048a0fd2b9..0000000000
--- a/tools/sci/old_objects.cpp
+++ /dev/null
@@ -1,679 +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 <sciresource.h>
-#include <console.h>
-#include <script.h>
-#include <vocabulary.h>
-#include <old_objects.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <util.h>
-#include <vm.h>
-#include <assert.h>
-
-#ifdef SCI_CONSOLE
-#define printf sciprintf
-/* Yeah, I shouldn't be doing this ;-) [CJR] */
-#endif
-
-FLEXARRAY_NOEXTRA(object*) fobjects;
-
-static int knames_count;
-static char** knames;
-static char** snames;
-static opcode* opcodes;
-
-object **object_map, *object_root;
-int max_object;
-
-const char* globals[] = {
- /*00*/
- "ego",
- "GAMEID",
- "roomXX",
- "speed",
- /*04*/
- "quitFlag",
- "cast",
- "regions",
- "timer",
- /*08*/
- "sounds",
- "inv",
- "eventHandler",
- "roomNumberExit",
- /*0C*/
- "previousRoomNumber",
- "roomNumber",
- "enterDebugModeOnRoomExit",
- "score",
- /*10*/
- "maximumScore",
- "11",
- "speed",
- "13",
- /*14*/
- "14",
- "loadCursor",
- "normalFont",
- "restoreSaveFont", /*dialogFont*/
- /*18*/
- "18",
- "19",
- "defaultFont",
- "1B",
- /*1C*/
- "pointerToVersionNumber",
- "locales",
- "pointerToSaveGameDirectory",
- "1F"
-};
-
-static int add_object(object* obj) {
- FLEXARRAY_APPEND(object*, fobjects, obj, return 1);
- return 0;
-}
-
-static void dump(byte* data, int len) {
- int i = 0;
- while (i < len) {
- printf("%02X ", data[i++]);
- if (i % 8 == 0) printf(" ");
- if (i % 16 == 0) printf("\n");
- }
- if (i % 16) printf("\n");
-}
-
-static void printMethod(object* obj, int meth, int indent) {
- script_method* m = obj->methods[meth];
- int i, j;
-
- for (j = 0; j < indent*2 - 1; j++) printf(" ");
- printf("Method %s\n", snames[m->number]);
-
- for (i = 0; i < m->used; i++) {
- script_opcode op = m->data[i];
-
- for (j = 0; j < indent; j++) printf(" ");
- printf("%s ", opcodes[op.opcode].name);
-
- switch (op.opcode) {
- case 0x21: { /*callk*/
- if (op.arg1 > knames_count) printf("<no such kernel function %02X> ", op.arg1);
- else printf("%s ", knames[op.arg1]);
- printf("%02X", op.arg2);
- }
- break;
- case 0x28: { /*class*/
- if (op.arg1 > max_object) printf("<no such class %02X>", op.arg1);
- else {
- /* [DJ] op.arg1+1 adjusts for the <root> object */
- if (fobjects.data[op.arg1+1] == 0) printf("<null object>");
- else printf("%s", fobjects.data[op.arg1+1]->name);
- }
- }
- break;
- case 0x44: {
- if (op.arg1 > 0x20) printf("<no such global %02X> ", op.arg1);
- else printf("%s ", globals[op.arg1]);
- }
- break;
- default: {
- int args[3];
- args[0] = op.arg1;
- args[1] = op.arg2;
- args[2] = op.arg3;
- for (j = 0; j < 3; j++) {
- switch (formats[op.opcode][j]) {
- case Script_Invalid: {
- printf("<invalid> ");
- }
- break;
- case Script_None: {
- j = 3;
- }
- break;
- case Script_SByte:
- case Script_Byte: {
- printf("%02X ", args[j]);
- }
- break;
- case Script_Word:
- case Script_SVariable:
- case Script_Variable:
- case Script_SRelative:
- case Script_Property:
- case Script_Global:
- case Script_Local:
- case Script_Temp:
- case Script_Param: {
- printf("%04X ", args[j]);
- }
- break;
- case Script_SWord: {
- if (args[j] < 0) printf("-%04X", -args[j]);
- else printf("%04X", args[j]);
- }
- break;
- case Script_End: {
- printf("\n");
- return;
- }
- break;
- default: {
- printf("<unknown arg type %d> ", formats[op.opcode][j]);
- }
- }
- }
- }
- break;
- }
- printf("\n");
- }
-}
-
-static void printObject_r(object* obj, int flags, int level) {
- int i;
- for (i = 0; i < level; i++) printf(" ");
- if (obj == 0) printf("(null)\n");
- else {
- printf("%s\n", obj->name);
- if (flags&SCRIPT_PRINT_METHODS) {
- for (i = 0; i < obj->method_count; i++) {
- printMethod(obj, i, level + 1);
- }
- }
- if (flags&SCRIPT_PRINT_CHILDREN) {
- for (i = 0; i < obj->children.used; i++) {
- printObject_r(obj->children.data[i], flags, level + 1);
- }
- }
- }
-}
-
-void printObject(object* obj, int flags) {
- printf("pO(%p, %d)\n", obj, flags);
- printObject_r(obj, flags, 0);
-}
-
-static object* object_new() {
- object* obj = (object*)sci_malloc(sizeof(object));
- if (obj == 0) return 0;
-
- obj->parent = 0;
- FLEXARRAY_INIT(object*, obj->children);
- obj->name = 0;
- obj->selector_count = 0;
- obj->selector_numbers = 0;
- obj->methods = 0;
- obj->method_count = 0;
-
- return obj;
-}
-
-static int add_child(object* parent, object* child) {
- FLEXARRAY_APPEND(object*, parent->children, child, return 1);
- return 0;
-}
-
-static object* fake_object(const char* reason) {
- object* obj = object_new();
- if (obj == 0) {
-#ifdef SCRIPT_DEBUG
- printf("object_new failed during fake for %s\n", reason);
-#endif
- free(obj);
- return 0;
- }
- if (add_child(object_root, obj)) {
-#ifdef SCRIPT_DEBUG
- printf("add_child failed during fake for %s\n", reason);
-#endif
- free(obj);
- return 0;
- }
- obj->name = reason;
- if (add_object(obj)) {
-#ifdef SCRIPT_DEBUG
- printf("add_object failed during fake for %s\n", reason);
-#endif
- /*FIXME: clean up parent*/
- return 0;
- }
- return obj;
-}
-
-static script_method* decode_method(byte* data) {
- script_method* m;
- int done = 0;
- int pos = 0;
- static int count = 0;
-
- count++;
-
- if ((m = (script_method*)sci_malloc(sizeof(script_method))) == 0) return 0;
- FLEXARRAY_INIT(script_opcode, *m);
-
- while (!done) {
- int op = data[pos] >> 1;
- int size = 2 - (data[pos] & 1);
- int* args[3];
- int arg;
- int old_pos;
-
- FLEXARRAY_ADD_SPACE(script_opcode, *m, 1, return 0);
- old_pos = pos;
- m->data[m->used-1].pos = pos;
- m->data[m->used-1].opcode = op;
-
- /*Copy the adresses of the args to an array for convenience*/
- args[0] = &m->data[m->used-1].arg1;
- args[1] = &m->data[m->used-1].arg2;
- args[2] = &m->data[m->used-1].arg3;
-
- /*Skip past the opcode*/
- pos++;
-
- for (arg = 0; arg < 4; arg++) {
- switch (formats[op][arg]) {
- case Script_Invalid: { /*Can't happen(tm)*/
- int i;
- printf("Invalid opcode %02X at %04X in method %d\n", op, pos, count);
- for (i = m->used - 9; i < m->used - 1; i++) {
- printf("%s[%02X] ", opcodes[m->data[i].opcode].name, m->data[i].opcode);
- dump(data + m->data[i].pos, m->data[i].size);
- }
- printf("Dump from %04X-%04X\n", pos - 16, pos + 16);
- dump(data + pos - 16, 32);
- }
- break;
- case Script_None: { /*No more args*/
- arg = 4;
- }
- break;
- case Script_Byte: /*Just a one byte arg*/
- case Script_SByte: {
- *args[arg] = data[pos++];
- }
- break;
- case Script_Word: { /*A two byte arg*/
- *args[arg] = getInt16(data + pos);
- pos += 2;
- }
- break;
- case Script_SWord: { /*A signed two-byte arg*/
- int t = getInt16(data + pos);
- if (t&0x8000) *args[arg] = -(t & 0x7FFF);
- else *args[arg] = t;
- pos += 2;
- }
- break;
- case Script_Variable: /*Size of arg depends on LSB in opcode*/
- case Script_SVariable:
- case Script_SRelative:
- case Script_Property:
- case Script_Global:
- case Script_Local:
- case Script_Temp:
- case Script_Param: {
- if (size == 1) *args[arg] = data[pos++];
- else {
- *args[arg] = getInt16(data + pos);
- pos += 2;
- }
- }
- break;
- case Script_End: { /*Special tag for ret*/
- done = 1;
- arg = 4;
- }
- break;
- default: { /*Can't happen(tm)*/
- printf("Unknown argument format %d for op %02X\n", formats[op][arg], op);
- }
- break;
- }
- }
- fflush(stdout);
- if (m->used) m->data[m->used-1].size = pos - old_pos;
- }
-
- return m;
-}
-
-#ifdef SCRIPT_DEBUG
-void list_code_blocks(resource_t* r) {
- int pos = getInt16(r->data + 2);
- while (pos < r->size - 2) {
- int type = getInt16(r->data + pos);
- int len = getInt16(r->data + pos + 2);
- if (type == 2) printf("%X-%X\n", pos, pos + len);
- pos += len;
- }
-}
-#endif
-
-
-/*These expect the frame, the whole frame, and, well, other stuff too,
- *I guess, as long as it looks like a frame*/
-static int get_type(unsigned char* obj) {
- return getInt16(obj);
-}
-
-static int get_length(unsigned char* obj) {
- return getInt16(obj + 2);
-}
-
-static int get_selector_count(unsigned char* obj) {
- return getInt16(obj + 10);
-}
-
-static int get_selector_value(unsigned char* obj, int sel) {
- assert(sel < get_selector_count(obj));
- return getInt16(obj + 12 + sel*2);
-}
-
-/*Bas things happen if the method offset value is wrong*/
-static unsigned char* get_method_area(unsigned char* obj) {
- return obj + getInt16(obj + 8) + 10;
-}
-
-static int get_method_count(unsigned char* obj) {
- return getInt16(get_method_area(obj));
-}
-
-static int get_method_number(unsigned char* obj, int i) {
- assert(i < get_method_count(obj));
- return getInt16(get_method_area(obj) + 2 + 2*i);
-}
-
-static int get_method_location(unsigned char* obj, int i) {
- assert(i < get_method_count(obj));
- return getInt16(get_method_area(obj) + 4 + 2*get_method_count(obj) + 2*i);
-}
-
-
-/*Returns the position of the first frame of type 'type' in resource 'r',
- *starting from the frame starting at 'start', or -1 on failure.
- */
-static int find_frame(resource_t* r, int type, unsigned int start) {
- int t = -1;
- unsigned int pos = start;
- unsigned char* frame;
-
- assert(start <= r->size - 4);
-
-#ifdef SCRIPT_DEBUG
- printf("Searching for frame of type %d in script %03d, starting at %#x\n", type, r->number, start);
- dump(r->data + start, 32);
-#endif
-
- /*Some times there's an extra byte at the beginning. Christoph?*/
-#if 1
- if (pos == 0 && r->size >= 6 && \
- !((0 < getInt16(r->data)) && (10 > getInt16(r->data)))) pos = 2;
-#else
- if (pos == 0)
- pos = 2;
-#endif
- frame = r->data + pos;
- while (1) {
-#ifdef SCRIPT_DEBUG
- printf("offset = %#x\n", pos);
- dump(frame, 32);
-#endif
- t = get_type(frame);
- if (t == type)
- break;
-
- if (t == 0)
- return -1;
-
- pos += get_length(frame);
- if (pos > (r->size - 2))
- return -1;
- frame += get_length(frame);
- }
-
- return pos;
-}
-
-
-
-/*FIXME: lots of things are identical to read_object and read_class. Some of
- *these would benefit from being put in separate functions.*/
-
-static object* read_object(resource_mgr_t *resmgr, int script, int positions[1000]) {
- resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
- unsigned char* raw;
- int pos;
- object* obj;
-
- printf("Searching for object in script %03d\n", script);
-
- if (r == 0) return 0;
-
- /*Skip to the next object*/
-#ifdef SCRIPT_DEBUG
- printf("pre skip: pos=%#x\n", positions[script]);
-#endif
- pos = find_frame(r, 1, positions[script]);
-#ifdef SCRIPT_DEBUG
- printf("post skip: pos=%#x\n", pos);
-#endif
- if (pos == -1) return 0;
- else positions[script] = pos + get_length(r->data + pos);
-#ifdef SCRIPT_DEBUG
- printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data + pos));
-#endif
-
- /*Construct the object*/
- obj = object_new();
- raw = r->data + pos;
-
- /*Fill in the name*/
- if (get_selector_count(raw) < 4) obj->name = "<anonymous>";
- else {
- if (get_selector_value(raw, 3))
- obj->name = (char *) r->data + get_selector_value(raw, 3);
- else obj->name = "<null>";
- }
-
- /*Fill in the class*/
- if (get_selector_count(raw) == 0) obj->parent = object_root;
- else {
- int parent_id = get_selector_value(raw, 1);
- if (parent_id >= fobjects.used) {
- free(obj);
- return 0;
- }
- if (parent_id < 1) obj->parent = object_root;
- else obj->parent = fobjects.data[parent_id];
- }
-
- /*Add the object to the class*/
- if (!obj->parent) {
- free(obj);
- return 0;
- }
- if (add_child(obj->parent, obj)) {
- free(obj);
- return 0;
- }
- if (add_object(obj)) {
- free(obj);
- return 0;
- }
-
- /*FIXME: decode selectors here*/
-
- obj->method_count = get_method_count(raw);
- obj->methods = (script_method**)sci_malloc(obj->method_count * sizeof(script_method));
- if (obj->methods == 0) {
- free(obj);
- return 0;
- } else {
- int i;
- for (i = 0; i < obj->method_count; i++) {
- int number = get_method_number(raw, i);
- int position = get_method_location(raw, i);
-
- if ((obj->methods[i] = decode_method(r->data + position)) == 0) {
- obj->method_count = i - 1;
- break;
- }
- obj->methods[i]->number = number;
- }
- }
-
- return obj;
-}
-
-static object* read_class(resource_mgr_t *resmgr, int script, int positions[1000]) {
- resource_t* r = scir_find_resource(resmgr, sci_script, script, 0);
- unsigned char* raw;
- int pos;
- object* obj;
-
- printf("Searching for class in script %03d\n", script);
-
- if (r == 0) return fake_object("<resource not found>");
-
- /*Skip to the next class*/
-#ifdef SCRIPT_DEBUG
- printf("pre skip: pos=%#x\n", positions[script]);
-#endif
- pos = find_frame(r, 6, positions[script]);
-#ifdef SCRIPT_DEBUG
- printf("post skip: pos=%#x\n", pos);
-#endif
- if (pos == -1) return fake_object("<no more classes in script>");
- else positions[script] = pos + get_length(r->data + pos);
-#ifdef SCRIPT_DEBUG
- printf("post post: pos=%#x (len=%#x)\n", positions[script], get_length(r->data + pos));
-#endif
-
- /*Construct the object*/
- obj = object_new();
- raw = r->data + pos;
-
- /*Fill in the name*/
- if (get_selector_count(raw) < 4) obj->name = "<anonymous>";
- else {
- if (get_selector_value(raw, 3))
- obj->name = (char *) r->data + get_selector_value(raw, 3);
- else obj->name = "<null>";
- }
-
- /*Fill in the parent*/
- if (get_selector_count(raw) == 0) obj->parent = object_root;
- else {
- int superclass_id = get_selector_value(raw, 1);
- printf("superclass==%d\n", superclass_id);
- if (superclass_id >= fobjects.used) {
- free(obj);
- return fake_object("<no such superclass>");
- }
- if (superclass_id < 1) obj->parent = object_root;
- else obj->parent = fobjects.data[superclass_id];
- }
-
- /*Add the class to the hierarchy*/
- if (!obj->parent) {
- free(obj);
- return fake_object("<null parent>");
- }
- if (add_child(obj->parent, obj)) {
- free(obj);
- return fake_object("<add_child failed>");
- }
- if (add_object(obj)) {
- free(obj);
- return fake_object("<add_object failed>");
- }
-
- /*FIXME: decode selectors and methods here*/
-
- return obj;
-}
-
-void freeObject(object* obj) {
- int i;
- for (i = 0; i < obj->children.used; i++) freeObject(obj->children.data[i]);
- free(obj);
-}
-
-static int objects_init(resource_mgr_t *resmgr) {
- FLEXARRAY_INIT(object*, fobjects);
- max_object = 0;
-
- if ((object_root = object_new()) == 0) return 1;
- object_root->name = "<root>";
- add_object(object_root);
-
- opcodes = vocabulary_get_opcodes(resmgr);
- knames = vocabulary_get_knames(resmgr, &knames_count);
- snames = vocabulary_get_snames(resmgr, NULL, 0);
-
- return 0;
-}
-
-int loadObjects(resource_mgr_t *resmgr) {
- int i;
- int *classes, class_count;
- int positions[1000];
-
- if (objects_init(resmgr)) {
-#ifdef SCRIPT_DEBUG
- perror("objects_init");
-#endif
- return 1;
- }
- classes = vocabulary_get_classes(resmgr, &class_count);
-
- for (i = 0; i < 1000; i++) positions[i] = 0;
- for (i = 0; i < class_count; i++) {
-#ifdef SCRIPT_DEBUG
- printf("\n\nReading class 0x%02X\n", i);
-#endif
- if (read_class(resmgr, classes[i], positions) == 0) {
-#ifdef SCRIPT_DEBUG
- fprintf(stderr, "Failed to load class %d, which is a parent.\n", i);
-#endif
- return 1;
- }
- }
-
- for (i = 0; i < 1000; i++) positions[i] = 0;
- for (i = 0; i < 1000; i++) while (read_object(resmgr, i, positions));
-
- object_map = fobjects.data;
- max_object = fobjects.used;
-
- return 0;
-}
-
-
diff --git a/tools/sci/old_objects.h b/tools/sci/old_objects.h
deleted file mode 100644
index fe653a7d3c..0000000000
--- a/tools/sci/old_objects.h
+++ /dev/null
@@ -1,72 +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 OLD_OBJECTS_H
-#define OLD_OBJECTS_H
-
-#include "common/scummsys.h"
-#include "engine/sci/include/sciresource.h"
-#include "engine/sci/include/util.h"
-
-typedef FLEXARRAY(script_opcode, int number;) script_method;
-
-typedef struct object_ {
- /*These are based on cached selector values, and set to the values
- *the selectors had at load time. If the selectors are changed in
- *instances, inconsistency will follow*/
- struct object_* parent;
- const char* name;
-
- FLEXARRAY_NOEXTRA(struct object_*) children;
-
- /*No flexarray, size the size is known from the start*/
- script_method** methods;
- int method_count;
-
- int selector_count;
- int* selector_numbers;
-} object;
-
-typedef struct {
- int id;
- object* classID;
- byte* heap;
- int offset;
-} instance;
-
-extern object **object_map, *object_root;
-extern int max_object;
-
-#define SCRIPT_PRINT_METHODS 1
-#define SCRIPT_PRINT_CHILDREN 2
-#define SCRIPT_PRINT_SELECTORS 3
-void printObject(object* obj, int flags);
-
-int loadObjects(resource_mgr_t *resmgr);
-void freeObject(object*);
-
-extern const char* globals[];
-
-#endif
diff --git a/tools/sci/scitrace.asm b/tools/sci/scitrace.asm
index 7891c414e6..360e0b7ffc 100644
--- a/tools/sci/scitrace.asm
+++ b/tools/sci/scitrace.asm
@@ -113,7 +113,7 @@ valid_open: mov bx, ax
mov ah, 3Eh
int 21h
-NowInstallTSR:
+NowInstallTSR:
mov ax, 2590h
mov dx, offset inthandler
int 21h ; int 90h pointer <- ds:dx
diff --git a/tools/sci/sciunpack.cpp b/tools/sci/sciunpack.cpp
deleted file mode 100644
index c9c50dc38b..0000000000
--- a/tools/sci/sciunpack.cpp
+++ /dev/null
@@ -1,473 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#include "sciunpack.h"
-
-#include <sciresource.h>
-#include <engine.h>
-#include <console.h>
-
-/* #define DRAW_GRAPHICS */
-
-#undef HAVE_OBSTACK_H
-
-#ifdef _MSC_VER
-# include <direct.h>
-# define extern __declspec(dllimport) extern
-#endif
-
-#ifdef HAVE_GETOPT_H
-# ifndef WIN32
-# include <getopt.h>
-# else
-# include <win32/getopt.h>
-# endif
-#endif /* HAVE_GETOPT_H */
-
-#ifdef DRAW_GRAPHICS
-# ifdef HAVE_LIBPNG
-# include "graphics_png.h"
-# endif /* HAVE_LIBPNG */
-#endif /* DRAW_GRAPHICS */
-
-#if defined (_MSC_VER) || defined (__BEOS__) || defined(__amigaos4__)
-/* [DJ] fchmod is not in Visual C++ RTL - and probably not needed,anyway */
-/* [RS] (see comment above, but read MS-DOS instead of Visual C++ RTL) */
-# define fchmod(file,mode)
-# define CREAT_OPTIONS O_BINARY
-#endif
-
-#ifndef CREAT_OPTIONS
-# define CREAT_OPTIONS 0x640
-#endif
-
-
-#define ACT_UNPACK 0
-#define ACT_WORDS 1
-#define ACT_LIST 2
-#define ACT_SCRIPTDUMP 3
-#define ACT_VOCABDUMP 4
-
-#define ACT_DEFAULT ACT_UNPACK
-
-static int conversion = 0;
-static int list = 0;
-static int verbose = 0;
-static int with_header = 1;
-static int color_mode = 0;
-static int action = ACT_DEFAULT;
-static guint8 midimask = 0x01; /* MT-32 */
-
-resource_mgr_t *resmgr;
-
-#ifdef WIN32
-#define fchmod(arg1, arg2)
-#endif
-
-void
-print_resource_filename(FILE* file, int type, int number) {
- if (resmgr->sci_version < SCI_VERSION_1)
- fprintf(file, "%s.%03d", sci_resource_types[type], number);
- else
- fprintf(file, "%d.%s", number, sci_resource_type_suffixes[type]);
-}
-
-void
-sprint_resource_filename(char* buf, int type, int number) {
- if (resmgr->sci_version < SCI_VERSION_1)
- sprintf(buf, "%s.%03d", sci_resource_types[type], number);
- else
- sprintf(buf, "%d.%s", number, sci_resource_type_suffixes[type]);
-}
-
-#ifdef HAVE_GETOPT_LONG
-static struct option options[] = {
- {"conversion", no_argument, &conversion, 1},
- {"version", no_argument, 0, 256},
- {"verbose", no_argument, &verbose, 1},
- {"help", no_argument, 0, 'h'},
- {"output-file", required_argument, 0, 'o'},
- {"unpack", no_argument, &action, ACT_UNPACK},
- {"list", no_argument, &action, ACT_LIST},
- {"words", no_argument, &action, ACT_WORDS},
- {"vocab", no_argument, &action, ACT_VOCABDUMP},
- {"objects", no_argument, &action, ACT_SCRIPTDUMP},
- {"with-header", no_argument, &with_header, 1},
- {"without-header", no_argument, &with_header, 0},
- {"sort-alpha", no_argument, &vocab_sort, SORT_METHOD_ALPHA},
- {"sort-group", no_argument, &vocab_sort, SORT_METHOD_GROUP},
-#ifdef DRAW_GRAPHICS
- {"palette-dither", no_argument, &color_mode, SCI_COLOR_DITHER},
- {"palette-interpolate", no_argument, &color_mode, SCI_COLOR_INTERPOLATE},
- {"palette-dither256", no_argument, &color_mode, SCI_COLOR_DITHER256},
-#endif /* DRAW_GRAPHICS */
- {"gamedir", required_argument, 0, 'd'},
- {"midimask", required_argument, 0, 'M'},
- {0, 0, 0, 0}
-};
-
-#endif /* HAVE_GETOPT_LONG */
-
-
-void unpack_resource(int stype, int snr, char *outfilename);
-
-
-int main(int argc, char** argv) {
- int retval = 0;
- int i;
- int stype = -1;
- int snr;
- char *resourcenumber_string = 0;
- char *outfilename = 0;
- int optindex = 0;
- int c;
- char *gamedir = sci_getcwd();
- int res_version = SCI_VERSION_AUTODETECT;
-
-#ifdef HAVE_GETOPT_LONG
- while ((c = getopt_long(argc, argv, "WOVUvhLcr:o:d:M:", options, &optindex)) > -1) {
-#else /* !HAVE_GETOPT_LONG */
- while ((c = getopt(argc, argv, "WOVUvhLcr:o:d:M:")) > -1) {
-#endif /* !HAVE_GETOPT_LONG */
-
- switch (c) {
- case 256:
- printf("sciunpack ("PACKAGE") "VERSION"\n");
- printf("This program is copyright (C) 1999, 2000, 2001 Christoph Reichenbach,\n"
- " Lars Skovlund, Magnus Reftel\n"
- "It comes WITHOUT WARRANTY of any kind.\n"
- "This is free software, released under the GNU General Public License.\n");
- exit(0);
-
- case 'h': {
- char *gcc_3_0_can_kiss_my_ass =
- "Usage: sciunpack [options] [-U] <resource.number>\n"
- " sciunpack [options] [-U] <resource> <number>\n"
- "Unpacks resource data\n"
- "If * is specified instead of <number>, \n"
- "all resources of given type will be unpacked.\n\n"
- " sciunpack [options] -W\n"
- "Lists vocabulary words\n\n"
- " sciunpack [options] -O\n"
- "Dumps the complete object hierarchy\n\n"
- " sciunpack [options] -V\n"
- "Prints selector names, opcodes, kernel names, and classes\n\n"
- "\nAvalable operations:\n"
- " --unpack -U Decompress resource\n"
- " --list -L List all resources\n"
- " --words -W List all vocabulary words\n"
- " --objects -O Print all objects\n"
- " --vocab -V Lists the complete vocabulary\n"
- "\nAvailable options:\n"
- "General:\n"
- " --version Prints the version number\n"
- " --verbose -v Enables additional output\n"
- " --help -h Displays this help message\n"
- " --midimask -M What 'play mask' to use. Defaults to MT-32 (0x01)\n"
-
- "Listing words:\n"
- " --sort-alpha sort in alphabetical order\n"
- " --sort-group sort in group order\n"
- "Unpacking:\n"
- " --convert -c Converts selected resources\n"
- " --output-file -o Selects output file\n"
- " --gamedir -d Read game resources from dir\n"
- " --with-header Forces the SCI header to be written (default)\n"
- " --without-header Prevents the two SCI header bytes from being written\n"
-#ifdef DRAW_GRAPHICS
- " --palette-dither Forces colors in 16 color games to be dithered\n"
- " --palette-interpolate Does color interpolation when drawing picture resources\n"
- " --palette-dither256 Does dithering in 256 colors\n"
-#endif /* DRAW_GRAPHICS */
- "\nAs a default, 'resource.number' is the output filename.\n"
- "If conversion is enabled, the following resources will be treated specially:\n"
- " sound resources: Will be converted to MIDI, stored in <number>.midi\n"
- " script resources: Will be dissected and stored in <number>.script\n"
-#ifdef DRAW_GRAPHICS
- " picture resources: Will be converted to PNG, stored in <number>.png\n"
-
-#endif /* DRAW_GRAPHICS */
- ;
-
- printf(gcc_3_0_can_kiss_my_ass);
- exit(0);
- }
-
- case 'v':
- verbose = 1;
- break;
-
- case 'L':
- action = ACT_LIST;
- break;
-
- case 'W':
- action = ACT_WORDS;
- break;
-
- case 'V':
- action = ACT_VOCABDUMP;
- break;
-
- case 'O':
- action = ACT_SCRIPTDUMP;
- break;
-
- case 'o':
- outfilename = optarg;
- break;
-
- case 'd':
- if (gamedir) sci_free(gamedir);
- gamedir = sci_strdup(optarg);
- break;
-
- case 'r':
- res_version = atoi(optarg);
- break;
-
- case 'c':
- conversion = 1;
- break;
-
- case 'M':
- midimask = (guint8) strtol(optarg, NULL, 0);
- break;
-
- case 0: /* getopt_long already did this for us */
- case '?':
- /* getopt_long already printed an error message. */
- break;
-
- default:
- return -1;
- }
- }
-
- if (action == ACT_UNPACK) {
- char *resstring = argv[optind];
-
- if (optind == argc) {
- fprintf(stderr, "Resource identifier required\n");
- return 1;
- }
-
- if ((resourcenumber_string = (char *) strchr(resstring, '.'))) {
- *resourcenumber_string++ = 0;
- } else if (optind + 1 == argc) {
- fprintf(stderr, "Resource number required\n");
- return 1;
- } else resourcenumber_string = argv[optind+1];
-
- for (i = 0; i < 18; i++)
- if ((strcmp(sci_resource_types[i], resstring) == 0)) stype = i;
- if (stype == -1) {
- printf("Could not find the resource type '%s'.\n", resstring);
- return 1;
- }
- } /* ACT_UNPACK */
-
- if (gamedir)
- if (chdir(gamedir)) {
- printf("Error changing to game directory '%s'\n", gamedir);
- exit(1);
- }
-
- if (!(resmgr = scir_new_resource_manager(gamedir, res_version,
- 0, 1024 * 128))) {
- fprintf(stderr, "Could not find any resources; quitting.\n");
- exit(1);
- }
-
- if (verbose) printf("Autodetect determined: %s\n",
- sci_version_types[resmgr->sci_version]);
-
-
- switch (action) {
-
- case ACT_LIST: {
- int i;
-
- if (verbose) {
- for (i = 0; i < resmgr->resources_nr; i++) {
- printf("%i: ", i);
- print_resource_filename(stdout,
- resmgr->resources[i].type,
- resmgr->resources[i].number);
- printf(" has size %i\n", resmgr->resources[i].size);
- }
-
- fprintf(stderr, " Reading complete. Actual resource count is %i\n",
- resmgr->resources_nr);
- } else {
- for (i = 0; i < resmgr->resources_nr; i++) {
- print_resource_filename(stdout,
- resmgr->resources[i].type,
- resmgr->resources[i].number);
- printf("\n");
- }
- }
- break;
- }
-
- case ACT_UNPACK: {
-
- if (!strcmp(resourcenumber_string, "*")) {
- int i;
- for (i = 0; i < resmgr->resources_nr; i++)
- if (resmgr->resources[i].type == stype)
- unpack_resource(stype, resmgr->resources[i].number, NULL);
- } else {
- snr = atoi(resourcenumber_string);
- unpack_resource(stype, snr, outfilename);
- }
- break;
- }
-
- case ACT_WORDS:
- retval = vocab_print();
- break;
-
- case ACT_SCRIPTDUMP:
- retval = script_dump();
- break;
-
- case ACT_VOCABDUMP:
- retval = vocab_dump();
- break;
-
- default:
- fprintf(stderr, "Invalid action %d- internal error!\n", action);
- return 1;
- }
-
-
- scir_free_resource_manager(resmgr);
- return retval;
-}
-
-
-void unpack_resource(int stype, int snr, char *outfilename) {
- char fnamebuffer[12]; /* stores default file name */
- resource_t *found;
-
- if ((stype == sci_sound) && conversion && (resmgr->sci_version > SCI_VERSION_0)) {
- fprintf(stderr, "MIDI conversion is only supported for SCI version 0\n");
- conversion = 0;
- }
-
- if (!outfilename) {
- outfilename = fnamebuffer;
- if ((stype == sci_sound) && conversion) {
-#ifdef HAVE_OBSTACK_H
- map_MIDI_instruments(resmgr);
-#endif
- sprintf(outfilename, "%03d.midi", snr);
- }
-#ifdef DRAW_GRAPHICS
- else if ((stype == sci_pic) && conversion)
- sprintf(outfilename, "%03d.png", snr);
-#endif /* DRAW_GRAPHICS */
- else
- sprint_resource_filename(outfilename, stype, snr);
- }
-
- if (verbose) {
- printf("seeking ");
- print_resource_filename(stdout, stype, snr);
- printf("...\n");
- }
-
- if ((found = scir_find_resource(resmgr, stype, snr, 0))) {
-
-#ifdef DRAW_GRAPHICS
- if ((stype == sci_pic) && conversion) {
- int i;
- picture_t pic = alloc_empty_picture(SCI_RESOLUTION_320X200, SCI_COLORDEPTH_8BPP);
- draw_pic0(pic, 1, 0, found->data);
- if ((i = write_pic_png(outfilename, pic->maps[0]))) {
- fprintf(stderr, "Writing the png failed (%d)\n", i);
- } else if (verbose) printf("Done.\n");
- free_picture(pic);
- } else
-#endif /* DRAW_GRAPHICS */
- if ((stype == sci_script) && conversion) {
- sprintf(outfilename, "%03d.script", snr);
- open_console_file(outfilename);
- script_dissect(resmgr, snr, NULL, 0);
- close_console_file();
- } else {
-
- /* Visual C++ doesn't allow to specify O_BINARY with creat() */
-#ifdef _MSC_VER
- int outf = open(outfilename, _O_CREAT | _O_BINARY | _O_RDWR);
-#else
- int outf = creat(outfilename, CREAT_OPTIONS);
-#endif
-
-#ifdef HAVE_OBSTACK_H
- if ((stype == sci_sound) && conversion) {
- int midilength;
- guint8 *outdata = makeMIDI0(found->data, &midilength, midimask);
- if (!outdata) {
- fprintf(stderr, "MIDI conversion failed. Aborting...\n");
- return;
- }
- if (verbose) printf("MIDI conversion from %d bytes of sound resource"
- " to a %d bytes MIDI file.\n",
- found->size, midilength);
- write(outf, outdata, midilength);
- free(outdata);
- } else {
-#endif /* HAVE_OBSTACK_H */
- guint8 header = 0x80 | found->type;
-
- if (with_header) {
- write(outf, &header, 1);
- header = 0x00;
- write(outf, &header, 1);
- }
-
- write(outf, found->data, found->size);
-#ifdef HAVE_OBSTACK_H
- }
-#endif /* HAVE_OBSTACK_H */
-
- fchmod(outf, 0644);
- close(outf);
- fchmod(outf, 0644);
-
- if (verbose) printf("Done.\n");
- }
-
- } else printf("Resource not found.\n");
-}
-
-
-
diff --git a/tools/sci/vocabdump.cpp b/tools/sci/vocabdump.cpp
deleted file mode 100644
index 26bac4dcf0..0000000000
--- a/tools/sci/vocabdump.cpp
+++ /dev/null
@@ -1,70 +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 <engine.h>
-#include "sciunpack.h"
-
-int
-vocab_dump() {
- char **names;
- opcode *opcodes;
- int i = 0, count;
- int *classes;
-
- printf("Selectors:\n");
- names = vocabulary_get_snames(resmgr, NULL, 0);
- while (names[i]) {
- printf("0x%02X: %s\n", i, names[i]);
- i++;
- }
- vocabulary_free_snames(names);
-
- i = 0;
- printf("\nOpcodes:\n");
- opcodes = vocabulary_get_opcodes(resmgr);
- while ((i < 256) && (opcodes[i].name)) {
- printf("%s: Type %i, Number %i\n", opcodes[i].name,
- opcodes[i].type, opcodes[i].number);
- i++;
- }
-
- names = vocabulary_get_knames(resmgr, &count);
- printf("\nKernel names:\n");
- if (names == 0) printf("Error loading kernel names\n");
- else {
- for (i = 0; i < count; i++) printf("0x%02X: %s\n", i, names[i]);
- vocabulary_free_knames(names);
- }
-
- classes = vocabulary_get_classes(resmgr, &count);
- printf("\nClasses:\n");
- if (classes == 0) printf("Error loading classes\n");
- else {
- for (i = 0; i < count; i++) printf("0x%02X: script %i\n", i, classes [i]);
- free(classes);
- }
-
- return 0;
-}
diff --git a/tools/scumm-md5.txt b/tools/scumm-md5.txt
index c3e48d6c5a..60fc615057 100644
--- a/tools/scumm-md5.txt
+++ b/tools/scumm-md5.txt
@@ -27,7 +27,7 @@
#
# Table of email addresse of contributors: Sometimes we need to add new
# information to this table, or need to verify the correctness of an
-# entry. To do this, it is vital that we (1) record from who a given
+# entry. To do this, it is vital that we (1) record from who a given
# piece of information comes, and (2) how to reach that person.
# Hopefully, this table will help a bit. Please extend it as needed.
#
@@ -255,8 +255,8 @@ monkey2 Monkey Island 2: LeChuck's Revenge
4cb9c3618f71668f8e4346c8f323fa82 10700 en Mac - - v1.0 11/5/92 Fingolfin
e246e02db9630533a40d99c9f54a8e01 -1 en Mac - - alt? Lars N&aelig;sbye Christensen
- da09e666fc8f5b78d7b0ac65d1a3b56e 11135 en FM-TOWNS - - - dhewg, Andrea Petrucci
- 430bc518017b6fac046f58bab6baad5d -1 jp FM-TOWNS - - - Antti Leimi, Andrea Petrucci
+ da09e666fc8f5b78d7b0ac65d1a3b56e 11135 en FM-TOWNS FM-TOWNS - - dhewg, Andrea Petrucci
+ 430bc518017b6fac046f58bab6baad5d -1 jp FM-TOWNS FM-TOWNS - - Antti Leimi, Andrea Petrucci
387a544b8b10b26912d8413bab63a853 -1 en DOS - Demo non-interactive khalek
@@ -281,13 +281,13 @@ atlantis Indiana Jones and the Fate of Atlantis
8e9417564f33790815445b2136efa667 11915 jp Mac - CD - Petr Maruska
e8d0697906e53fee8b7e9f5652696da8 11915 jp DOS - CD - Petr Maruska, tracker #3017219
- c7be10f775404fd9785a8b92a06d240c 12030 en FM-TOWNS - - - dhewg, Andrea Petrucci
- 4d34042713958b971cb139fba4658586 -1 jp FM-TOWNS - - - Andrea Petrucci
+ c7be10f775404fd9785a8b92a06d240c 12030 en FM-TOWNS FM-TOWNS - - dhewg, Andrea Petrucci
+ 4d34042713958b971cb139fba4658586 -1 jp FM-TOWNS FM-TOWNS - - Andrea Petrucci
035deab53b47bc43abc763560d0f8d4b -1 en DOS Floppy Demo -
98744fe66ff730e8c2b3b1f58803ab0b -1 en DOS Floppy Demo - Simon Krumrein, sev
99b6f822b0b2612415407865438697d6 -1 en DOS - Demo non-interactive
- 28d24a33448fab6795850bc9f159a4a2 11170 jp FM-TOWNS - Demo non-interactive khalek, Fingolfin
+ 28d24a33448fab6795850bc9f159a4a2 11170 jp FM-TOWNS FM-TOWNS Demo non-interactive khalek, Fingolfin
tentacle Day of the Tentacle
acad97ab1c6fc2a5b2d98abf6db4a190 -1 en All? Floppy Floppy Version A ?
@@ -392,7 +392,7 @@ football Backyard Football
football2002 Backyard Football 2002
a095616d2d23ccf43b8e257711202cba -1 en All - - - clone2727
d0549508a06bbb9f99ed19c9e97891f3 -1 en All - - - Kirben
- 2e85f7aa054930c692a5b1bed1dfc295 -1 en All - Demo - khalek
+ 2e85f7aa054930c692a5b1bed1dfc295 -1 en All - Patched - khalek
soccer Backyard Soccer
701246819d1a70573f41bf33fc19214f -1 en All - - - sev
@@ -745,7 +745,7 @@ puttputt Putt-Putt Joins the Parade
6a30a07f353a75cdc602db27d73e1b42 -1 en Windows HE 70 - - khalek
31aa57f460a3d12429f0552a46a90b39 6150 en DOS Demo Demo -
- f40a7f495f59188ca57a9d1d50301bb6 -1 en Mac Demo Demo - khalek
+ f40a7f495f59188ca57a9d1d50301bb6 -1 en Mac HE 60 Demo - khalek
37ff1b308999c4cca7319edfcc1280a0 8269 en Windows HE 70 Demo - khalek
puttzoo Putt-Putt Saves the Zoo
diff --git a/tools/skycpt/AsciiCptCompile.cpp b/tools/skycpt/AsciiCptCompile.cpp
index 22711bf061..1169de5aea 100644
--- a/tools/skycpt/AsciiCptCompile.cpp
+++ b/tools/skycpt/AsciiCptCompile.cpp
@@ -56,8 +56,8 @@ int main(int argc, char* argv[])
printf("Sorry, this program only works on little endian systems.\nGoodbye.\n");
return 0;
}
- TextFile *cptDef = new TextFile("compact.txt");
- FILE *inf = fopen("compact.txt", "r");
+ TextFile *cptDef = new TextFile("COMPACT.TXT");
+ FILE *inf = fopen("COMPACT.TXT", "r");
FILE *dbg = fopen("compact.dbg", "wb");
FILE *out = fopen("compact.bin", "wb");
FILE *sve = fopen("savedata.txt", "r");
@@ -70,7 +70,3 @@ int main(int argc, char* argv[])
printf("done\n");
return 0;
}
-
-
-
-
diff --git a/tools/skycpt/AsciiCptCompile.vcproj b/tools/skycpt/AsciiCptCompile.vcproj
index 99a2524cc9..e6a5def316 100644
--- a/tools/skycpt/AsciiCptCompile.vcproj
+++ b/tools/skycpt/AsciiCptCompile.vcproj
@@ -387,7 +387,7 @@
RelativePath=".\asmSrc\9compact.inc">
</File>
<File
- RelativePath=".\Compact.txt">
+ RelativePath=".\COMPACT.TXT">
</File>
<File
RelativePath=".\savedata.txt">
diff --git a/tools/skycpt/KmpSearch.h b/tools/skycpt/KmpSearch.h
index 3e1655a37b..6f13ace4a5 100644
--- a/tools/skycpt/KmpSearch.h
+++ b/tools/skycpt/KmpSearch.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef __KmpSearch__
-#define __KmpSearch__
+#ifndef SKYCPT_KMPSEARCH_H
+#define SKYCPT_KMPSEARCH_H
class KmpSearch {
public:
diff --git a/tools/skycpt/README b/tools/skycpt/README
index f6d5f2a45b..4b303cb0e9 100644
--- a/tools/skycpt/README
+++ b/tools/skycpt/README
@@ -32,17 +32,24 @@ If you still want to waste your time by creating this file:
v.0.00365: CD Demo
v.0.00368: Full CD version
v.0.00372: Final CD version
-4) Change scummvm/sky/logic.cpp, function fnSkipIntroCode
- so that it calls _skyControl->doLoadSavePanel()
+4) Apply the supplied skycpt-engine.patch to a clean copy of the ScummVM
+ codebase. This modifies the sky engine to allow savefiles to be generated
+ containing the required data for the skycpt tool.
5) Compile the patched ScummVM.
-6) Start each of the BASS versions, enjoy the intro over and over again.
+6) Start each of the BASS versions with this patched version, enjoy the intro
+ over and over again.
Afterwards, it'll automatically show the load/save dialog where you save the game.
-7) Rename the Savegame files you created to "RESET.*", depending on the version.
+7) Rename the Savegame files you created (SKY-VM.000 etc., not the ASD or SAV
+ file) to "RESET.*", depending on the version.
e.g. RESET.288 for v.0.00288
-8) Copy the files into the skycpt tool directory, execute the tool again.
+8) Rename each file with a ".gz" suffix and run gunzip on it.
+ This removes the ScummVM gzip savefile compression.
+ Failing to do this will cause skycpt to abort at a filesize assert
+ when reading the "RESET.*" files.
+9) Copy the files into the skycpt tool directory, execute the tool again.
It'll create a new COMPACT.DBG.
-9) Rename this file to SKY.CPT.
-10) DELETE IT BECAUSE IT'S PROBABLY BROKEN, NOT WORTH BOTHERING WITH ANYWAYS
+10) Rename this file to SKY.CPT.
+11) DELETE IT BECAUSE IT'S PROBABLY BROKEN, NOT WORTH BOTHERING WITH ANYWAYS
AND DOWNLOAD THE SKY.CPT FILE FROM THE URL ABOVE!!
Oh, I almost forgot.
diff --git a/tools/skycpt/TextFile.cpp b/tools/skycpt/TextFile.cpp
index 92d747f955..ee64d22f5f 100644
--- a/tools/skycpt/TextFile.cpp
+++ b/tools/skycpt/TextFile.cpp
@@ -28,13 +28,14 @@
TextFile::TextFile(const char *name) {
FILE *inf = fopen(name, "r");
+ _lines = NULL;
if (!inf) {
printf("Unable to open file %s\n", name);
getchar();
+ } else {
+ read(inf);
+ fclose(inf);
}
- _lines = NULL;
- read(inf);
- fclose(inf);
}
uint32 crop(char *line) {
diff --git a/tools/skycpt/TextFile.h b/tools/skycpt/TextFile.h
index 92b1546167..a9ad65417c 100644
--- a/tools/skycpt/TextFile.h
+++ b/tools/skycpt/TextFile.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef __TextFile__
-#define __TextFile__
+#ifndef SKYCPT_TEXTFILE_H
+#define SKYCPT_TEXTFILE_H
#include "stdafx.h"
#include <stdio.h>
diff --git a/tools/skycpt/cptcompiler.cpp b/tools/skycpt/cptcompiler.cpp
index 75434a4618..956f575d7d 100644
--- a/tools/skycpt/cptcompiler.cpp
+++ b/tools/skycpt/cptcompiler.cpp
@@ -475,7 +475,7 @@ void doCompile(FILE *inf, FILE *debOutf, FILE *resOutf, TextFile *cptDef, FILE *
bool filesExist = true;
char inName[32];
for (int i = 0; i < 7; i++) {
- sprintf(inName, "reset.%03d", gameVers[i]);
+ sprintf(inName, "RESET.%03d", gameVers[i]);
FILE *test = fopen(inName, "rb");
if (test)
fclose(test);
@@ -505,8 +505,9 @@ void doCompile(FILE *inf, FILE *debOutf, FILE *resOutf, TextFile *cptDef, FILE *
fwrite(&tmp, 2, 1, debOutf);
printf("reset destination: %ld\n", ftell(debOutf));
for (int cnt = 0; cnt < 6; cnt++) {
+ printf("Processing diff v0.0%03d\n", gameVers[cnt]);
uint16 diffPos = 0;
- sprintf(inName, "reset.%03d", gameVers[cnt]);
+ sprintf(inName, "RESET.%03d", gameVers[cnt]);
FILE *resDiff = fopen(inName, "rb");
fseek(resDiff, 0, SEEK_END);
assert(ftell(resDiff) == (resSize * 2));
diff --git a/tools/skycpt/skycpt-engine.patch b/tools/skycpt/skycpt-engine.patch
new file mode 100644
index 0000000000..16388a3221
--- /dev/null
+++ b/tools/skycpt/skycpt-engine.patch
@@ -0,0 +1,67 @@
+Index: engines/sky/compact.cpp
+===================================================================
+--- engines/sky/compact.cpp (revision 53162)
++++ engines/sky/compact.cpp (working copy)
+@@ -138,11 +138,15 @@
+ if (fileVersion != 0)
+ error("unknown \"sky.cpt\" version");
+
++#if 0
++ // Disabled as sky.cpt / compact.dbg used during RESET.* generation is
++ // a different size from expected.
+ if (SKY_CPT_SIZE != _cptFile->size()) {
+ GUI::MessageDialog dialog("The \"sky.cpt\" file has an incorrect size.\nPlease (re)download it from www.scummvm.org", "OK", NULL);
+ dialog.runModal();
+ error("Incorrect sky.cpt size (%d, expected: %d)", _cptFile->size(), SKY_CPT_SIZE);
+ }
++#endif
+
+ // set the necessary data structs up...
+ _numDataLists = _cptFile->readUint16LE();
+Index: engines/sky/control.cpp
+===================================================================
+--- engines/sky/control.cpp (revision 53162)
++++ engines/sky/control.cpp (working copy)
+@@ -416,8 +416,8 @@
+ }
+
+ void Control::doLoadSavePanel() {
+- if (SkyEngine::isDemo())
+- return; // I don't think this can even happen
++ //if (SkyEngine::isDemo())
++ // return; // I don't think this can even happen
+ initPanel();
+ _skyScreen->clearScreen();
+ if (SkyEngine::_systemVars.gameVersion < 331)
+@@ -427,12 +427,17 @@
+
+ _savedMouse = _skyMouse->giveCurrentMouseType();
+ _savedCharSet = _skyText->giveCurrentCharSet();
+- _skyText->fnSetFont(2);
++ // Demo lacks any fonts apart from 0
++ if (SkyEngine::isDemo())
++ _skyText->fnSetFont(0);
++ else
++ _skyText->fnSetFont(2);
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+ _lastButton = -1;
+ _curButtonText = 0;
+
+- saveRestorePanel(false);
++ // Parameter modified to true to ensure save dialog, not load is shown
++ saveRestorePanel(true);
+
+ memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+Index: engines/sky/logic.cpp
+===================================================================
+--- engines/sky/logic.cpp (revision 53162)
++++ engines/sky/logic.cpp (working copy)
+@@ -2376,6 +2376,7 @@
+
+ bool Logic::fnSkipIntroCode(uint32 a, uint32 b, uint32 c) {
+ SkyEngine::_systemVars.pastIntro = true;
++ _skyControl->doLoadSavePanel();
+ return true;
+ }
+
diff --git a/tools/skycpt/stdafx.h b/tools/skycpt/stdafx.h
index f5016cf98a..3e5c6042eb 100644
--- a/tools/skycpt/stdafx.h
+++ b/tools/skycpt/stdafx.h
@@ -27,8 +27,8 @@
// oder projektspezifische Includedateien, die häufig benutzt, aber
// in unregelmäßigen Abständen geändert werden.
//
-#ifndef __STDAFX_H__
-#define __STDAFX_H__
+#ifndef SKYCPT_STDAFX_H
+#define SKYCPT_STDAFX_H
#ifdef _MSC_VER
#pragma once
diff --git a/tools/svnpropset.sh b/tools/svnpropset.sh
index 46acba534e..a6e331956c 100755
--- a/tools/svnpropset.sh
+++ b/tools/svnpropset.sh
@@ -1,12 +1,12 @@
#!/bin/sh
-
+
# This script adds common svn properties to files
-
+
if [ $# -eq 0 ]; then
echo "Usage: $0 [FILE]..."
exit 1
fi
-
+
for filename in $@; do
if [ -f $filename ]; then
svn propset svn:mime-type text/plain $filename
diff --git a/tools/themeparser.py b/tools/themeparser.py
index 7150fa02ed..993f7c79bc 100644
--- a/tools/themeparser.py
+++ b/tools/themeparser.py
@@ -51,70 +51,70 @@ def pbin(data):
class STXBinaryFile(object):
class InvalidRGBColor(Exception):
pass
-
+
class InvalidResolution(Exception):
pass
-
+
class InvalidFontIdentifier(Exception):
pass
-
+
class InvalidBitmapName(Exception):
pass
-
+
class InvalidDialogOverlay(Exception):
pass
-
+
class DrawStepData(object):
def __init__(self, isDefault, packFormat, function):
self.isDefault = isDefault
self.packFormat = packFormat
self.parse = function
-
+
BYTEORDER = '>' # big-endian format by default, platform independent
-
+
TRUTH_VALUES = {"" : 0, "true" : 1, "false" : 0, "True" : 1, "False" : 0}
-
+
BLOCK_HEADERS = {
"bitmaps" : 0x0100000A,
"fonts" : 0x0100000B,
"cursor" : 0x0100000C,
"drawdata" : 0x0100000D,
-
+
"globals" : 0x0200000A,
"dialog" : 0x0200000B,
}
-
+
DIALOG_shading = {"none" : 0x0, "dim" : 0x1, "luminance" : 0x2}
-
+
DS_triangleOrientations = {"top" : 0x1, "bottom" : 0x2, "left" : 0x3, "right" : 0x4}
DS_fillModes = {"none" : 0x0, "foreground" : 0x1, "background" : 0x2, "gradient" : 0x3}
DS_vectorAlign = {"left" : 0x1, "right" : 0x2, "bottom" : 0x3, "top" : 0x4, "center" : 0x5}
-
+
DS_functions = {
- "void" : 0x0, "circle" : 0x1, "square" : 0x2, "roundedsq" : 0x3, "bevelsq" : 0x4,
+ "void" : 0x0, "circle" : 0x1, "square" : 0x2, "roundedsq" : 0x3, "bevelsq" : 0x4,
"line" : 0x5, "triangle" : 0x6, "fill" : 0x7, "tab" : 0x8, "bitmap" : 0x9, "cross" : 0xA
}
-
+
DS_fonts = {
- "text_default" : 0x0, "text_hover" : 0x1, "text_disabled" : 0x2,
+ "text_default" : 0x0, "text_hover" : 0x1, "text_disabled" : 0x2,
"text_inverted" : 0x3, "text_button" : 0x4, "text_button_hover" : 0x5, "text_normal" : 0x6
}
-
+
DS_text_alignV = {"bottom" : 0x0, "center" : 0x1, "top" : 0x2}
DS_text_alignH = {"left" : 0x0, "center" : 0x1, "right" : 0x2}
-
+
DS_list = [
"func", "fill", "stroke", "gradient_factor",
"width", "height", "xpos", "ypos",
"radius", "bevel", "shadow", "orientation", "file",
"fg_color", "bg_color", "gradient_start", "gradient_end", "bevel_color"
]
-
+
def __init__(self, themeName, autoLoad = True, verbose = False):
self._themeName = themeName
self._stxFiles = []
self._verbose = verbose
-
+
self.DS_data = {
# attribute name isDefault pack parse function
"func" : self.DrawStepData(False, "B", lambda f: self.DS_functions[f]),
@@ -136,74 +136,74 @@ class STXBinaryFile(object):
"gradient_end" : self.DrawStepData(True, "4s", self.__parseColor),
"bevel_color" : self.DrawStepData(True, "4s", self.__parseColor)
}
-
+
if autoLoad:
if not os.path.isdir(themeName) or not os.path.isfile(os.path.join(themeName, "THEMERC")):
raise IOError
-
+
for filename in os.listdir(themeName):
filename = os.path.join(themeName, filename)
if os.path.isfile(filename) and filename.endswith('.stx'):
self._stxFiles.append(filename)
-
+
def debug(self, text):
if self._verbose: print (text)
-
+
def debugBinary(self, data):
if self._verbose:
print ("BINARY OUTPUT (%d bytes): %s" % (len(data), " ".join(["%0.2X" % ord(c) for c in data])))
-
+
def addSTXFile(self, filename):
if not os.path.isfile(filename):
raise IOError
else:
- self._stxFiles.append(filename)
-
+ self._stxFiles.append(filename)
+
def parse(self):
if not self._stxFiles:
self.debug("No files have been loaded for parsing on the theme.")
raise IOError
-
+
for f in self._stxFiles:
self.debug("Parsing %s." % f)
with open(f) as stxFile:
self.__parseFile(stxFile)
-
+
def __parseFile(self, xmlFile):
stxDom = DOM.parse(xmlFile)
-
+
for layout in stxDom.getElementsByTagName("layout_info"):
self.__parseLayout(layout)
for render in stxDom.getElementsByTagName("render_info"):
self.__parseRender(render)
-
+
stxDom.unlink()
-
+
def __getBitmap(self, bmp):
bmp = str(bmp)
-
+
if bmp == "":
return 0x0
if bmp not in self._bitmaps:
raise self.InvalidBitmapName
-
+
return self._bitmaps[bmp]
-
+
def __parseDrawStep(self, drawstepDom, localDefaults = {}):
-
+
dstable = {}
-
+
if drawstepDom.tagName == "defaults":
isGlobal = drawstepDom.parentNode.tagName == "render_info"
-
+
for ds in self.DS_list:
if self.DS_data[ds].isDefault and drawstepDom.hasAttribute(ds):
dstable[ds] = self.DS_data[ds].parse(drawstepDom.getAttribute(ds))
-
+
elif isGlobal:
dstable[ds] = None
-
+
else:
for ds in self.DS_data:
if drawstepDom.hasAttribute(ds):
@@ -212,13 +212,13 @@ class STXBinaryFile(object):
dstable[ds] = localDefaults[ds] if ds in localDefaults else self._globalDefaults[ds]
else:
dstable[ds] = None
-
+
return dstable
-
-
+
+
def __parseDrawStepToBin(self, stepDict):
"""
- /BBBBiiiiiBBB4s4s4s4s4sB/ ==
+ /BBBBiiiiiBBB4s4s4s4s4sB/ ==
function (byte)
fill (byte)
stroke (byte)
@@ -238,10 +238,10 @@ class STXBinaryFile(object):
gradient_end (4 byte)
bevel_color (4 byte)
"""
-
+
packLayout = ""
packData = []
-
+
for ds in self.DS_list:
layout = self.DS_data[ds].packFormat
data = stepDict[ds]
@@ -249,18 +249,18 @@ class STXBinaryFile(object):
if not data:
size = struct.calcsize(layout)
packLayout += "B" * size
-
+
for d in range(size):
packData.append(0)
else:
packLayout += layout
packData.append(data)
-
-
+
+
stepBin = struct.pack(self.BYTEORDER + packLayout, *tuple(packData))
return stepBin
-
-
+
+
def __parseResolutionToBin(self, resString):
"""
/B bHH bHH bHH/ == 1 byte + x * 9 bytes
@@ -269,34 +269,34 @@ class STXBinaryFile(object):
resolution X (half)
resolution Y (half)
"""
-
+
if resString == "":
return struct.pack(self.BYTEORDER + "BbHH", 1, 0, 0, 0)
-
+
resolutions = resString.split(", ")
packFormat = "B" + "bHH" * len(resolutions)
packData = [len(resolutions)]
-
+
for res in resolutions:
exclude = 0
if res[0] == '-':
exclude = 1
res = res[1:]
-
+
try:
x, y = res.split('x')
x = 0 if x == 'X' else int(x)
y = 0 if y == 'Y' else int(y)
except ValueError:
raise InvalidResolution
-
+
packData.append(exclude)
packData.append(x)
packData.append(y)
-
+
buff = struct.pack(self.BYTEORDER + packFormat, *tuple(packData))
return buff
-
+
def __parseRGBToBin(self, color):
"""
/xBBB/ == 32 bits
@@ -305,24 +305,24 @@ class STXBinaryFile(object):
green color (byte)
blue color (byte)
"""
-
+
try:
rgb = tuple(map(int, color.split(", ")))
except ValueError:
raise self.InvalidRGBColor
-
+
if len(rgb) != 3:
raise self.InvalidRGBColor
-
+
for c in rgb:
if c < 0 or c > 255:
raise self.InvalidRGBColor
-
+
rgb = struct.pack(self.BYTEORDER + "xBBB", *tuple(rgb))
-
+
# self.debugBinary(rgb)
return rgb
-
+
def __parseColor(self, color):
try:
color = self.__parseRGBToBin(color)
@@ -330,93 +330,93 @@ class STXBinaryFile(object):
if color not in self._colors:
raise self.InvalidRGBColor
color = self._colors[color]
-
+
return color
-
-
+
+
def __parsePalette(self, paletteDom):
- self._colors = {}
-
+ self._colors = {}
+
for color in paletteDom.getElementsByTagName("color"):
color_name = color.getAttribute('name')
color_rgb = self.__parseRGBToBin(color.getAttribute('rgb'))
-
+
self._colors[color_name] = color_rgb
# self.debug("COLOR: %s" % (color_name))
-
-
+
+
def __parseBitmaps(self, bitmapsDom):
self._bitmaps = {}
idCount = 0xA0
packLayout = ""
packData = []
-
+
for bitmap in bitmapsDom.getElementsByTagName("bitmap"):
bmpName = str(bitmap.getAttribute("filename"))
self._bitmaps[bmpName] = idCount
-
+
packLayout += "B%ds" % (len(bmpName) + 1)
packData.append(idCount)
packData.append(bmpName)
idCount += 1
-
-
+
+
bitmapBinary = struct.pack(
self.BYTEORDER + "IB" + packLayout,
self.BLOCK_HEADERS['bitmaps'],
len(bitmapsDom.getElementsByTagName("bitmap")),
*tuple(packData)
)
-
+
# self.debug("BITMAPS:\n%s\n\n" % pbin(bitmapBinary))
return bitmapBinary
-
+
def __parseFonts(self, fontsDom):
"""
/IB Bs4ss .../
section header (uint32)
number of font definitions (byte)
-
+
id for font definition (byte)
resolution for font (byte array)
color for font (4 bytes)
font filename (byte array, null terminated)
"""
-
+
packLayout = ""
packData = []
for font in fontsDom.getElementsByTagName("font"):
ident = font.getAttribute("id")
-
+
if ident not in self.DS_fonts:
raise self.InvalidFontIdentifier
-
+
color = self.__parseColor(font.getAttribute("color"))
filename = str(font.getAttribute("file"))
-
+
if filename == 'default':
filename = ''
-
+
resolution = self.__parseResolutionToBin(font.getAttribute("resolution"))
-
+
packLayout += "B%ds4s%ds" % (len(resolution), len(filename) + 1)
packData.append(self.DS_fonts[ident])
packData.append(resolution)
packData.append(color)
- packData.append(filename)
-
+ packData.append(filename)
+
fontsBinary = struct.pack(self.BYTEORDER + \
- "IB" + packLayout,
- self.BLOCK_HEADERS['fonts'],
+ "IB" + packLayout,
+ self.BLOCK_HEADERS['fonts'],
len(fontsDom.getElementsByTagName("font")),
*tuple(packData)
)
-
-
+
+
# self.debug("FONTS DATA:\n%s\n\n" % pbin(fontsBinary))
return fontsBinary
-
+
def __parseTextToBin(self, textDom):
"""
/BBBx/
@@ -425,19 +425,19 @@ class STXBinaryFile(object):
horizontal alignment (byte)
padding until word (byte)
"""
-
+
font = textDom.getAttribute("font")
if font not in self.DS_fonts:
raise self.InvalidFontIdentifier
-
+
textBin = struct.pack(self.BYTEORDER + "BBBx",
self.DS_fonts[font],
self.DS_text_alignV[textDom.getAttribute("vertical_align")],
self.DS_text_alignH[textDom.getAttribute("horizontal_align")]
)
-
+
return textBin
-
+
def __parseDrawData(self, ddDom):
"""
/IsIBBHss/
@@ -450,32 +450,32 @@ class STXBinaryFile(object):
** text segment (4 bytes)
drawstep segments (byte array)
"""
-
-
+
+
localDefaults = ddDom.getElementsByTagName("defaults")
localDefaults = localDefaults[0] if localDefaults else {}
-
+
stepList = []
-
+
for ds in ddDom.getElementsByTagName("drawstep"):
dstable = self.__parseDrawStep(ds, localDefaults)
dsbinary = self.__parseDrawStepToBin(dstable)
-
+
stepList.append(dsbinary)
stepByteArray = "".join(stepList)
-
+
resolution = self.__parseResolutionToBin(ddDom.getAttribute("resolution"))
-
+
text = ddDom.getElementsByTagName("text")
text = self.__parseTextToBin(text[0]) if text else ""
-
+
id_hash = str.__hash__(str(ddDom.getAttribute("id"))) & 0xFFFFFFFF
cached = self.TRUTH_VALUES[ddDom.getAttribute("cached")]
-
+
ddBinary = struct.pack(self.BYTEORDER + \
"I%dsIBBH4s%ds" % (len(resolution), len(stepByteArray)),
-
+
self.BLOCK_HEADERS['drawdata'], # Section Header (uint32)
resolution, # Resolution (byte array, word-aligned)
id_hash, # DrawData id hash (uint32)
@@ -485,10 +485,10 @@ class STXBinaryFile(object):
text, # ** text segment (byte array)
stepByteArray # drawstep segments (byte array)
)
-
+
# self.debug("DRAW DATA %s (%X): \n" % (ddDom.getAttribute("id"), id_hash) + pbin(ddBinary) + "\n\n")
return ddBinary
-
+
def __parseCursor(self, cursorDom):
"""
/IsBBhh/
@@ -499,11 +499,11 @@ class STXBinaryFile(object):
hotspot X (half)
hotspot Y (half)
"""
-
+
resolution = self.__parseResolutionToBin(cursorDom.getAttribute("resolution"))
scale = int(cursorDom.getAttribute("scale"))
hsX, hsY = cursorDom.getAttribute("hotspot").split(", ")
-
+
cursorBin = struct.pack(self.BYTEORDER + "I%dsBBhh" % len(resolution),
self.BLOCK_HEADERS['cursor'],
resolution,
@@ -512,19 +512,19 @@ class STXBinaryFile(object):
int(hsX),
int(hsY)
)
-
+
# self.debug("CURSOR:\n%s\n\n" % pbin(cursorBin))
return cursorBin
-
+
def __parseDialog(self, dialogDom):
-
+
dialog_id = str(dialogDom.getAttribute("name"))
resolution = self.__parseResolutionToBin(dialogDom.getAttribute("resolution"))
-
+
overlays = str(dialogDom.getAttribute("overlays"))
overlay_type = 0x0
overlay_parent = ""
-
+
if overlays == "screen":
overlay_type = 0x1
elif overlays == "screen_center":
@@ -532,19 +532,19 @@ class STXBinaryFile(object):
else:
overlay_type = 0x3
overlay_parent = str(overlays)
-
+
dialog_enabled = 0x1
if dialogDom.hasAttribute("enabled"):
dialog_enabled = self.TRUTH_VALUES[dialogDom.getAttribute("enabled")]
-
+
dialog_shading = 0x0
if dialogDom.hasAttribute("shading"):
dialog_shading = self.DIALOG_shading[dialogDom.getAttribute("shading")]
-
+
dialog_inset = 0
if dialogDom.hasAttribute("inset"):
dialog_inset = int(dialogDom.getAttribute("inset"))
-
+
dialogBin = struct.pack(self.BYTEORDER + \
"I%ds%dsBBBB%ds" % (len(resolution), len(dialog_id) + 1, len(overlay_parent) + 1),
self.BLOCK_HEADERS['dialog'],
@@ -556,51 +556,51 @@ class STXBinaryFile(object):
overlay_type,
overlay_parent,
)
-
+
return dialogBin
-
+
def __parseLayout(self, layoutDom):
self.debug("GLOBAL SECTION: LAYOUT INFO.")
-
+
dialogBIN = ""
-
+
for dialog in layoutDom.getElementsByTagName("dialog"):
dialogBIN += self.__parseDialog(dialog)
-
-
+
+
printBinaryDump(dialogBIN)
-
+
return dialogBIN
-
+
def __parseRender(self, renderDom):
self.debug("GLOBAL SECTION: RENDER INFO.")
-
+
bitmapBIN = ""
fontsBIN = ""
cursorBIN = ""
drawdataBIN = ""
-
+
# parse color palettes
paletteDom = renderDom.getElementsByTagName("palette")
if paletteDom:
self.__parsePalette(paletteDom[0])
-
+
# parse bitmaps
bitmapsDom = renderDom.getElementsByTagName("bitmaps")
if bitmapsDom:
bitmapBIN = self.__parseBitmaps(bitmapsDom[0])
-
+
# parse fonts
fontsDom = renderDom.getElementsByTagName("fonts")[0]
fontsBIN = self.__parseFonts(fontsDom)
-
+
# parse defaults
defaultsDom = renderDom.getElementsByTagName("defaults")
if defaultsDom:
self._globalDefaults = self.__parseDrawStep(defaultsDom[0])
else:
self._globalDefaults = {}
-
+
# parse cursors
for cur in renderDom.getElementsByTagName("cursor"):
cursorBIN += self.__parseCursor(cur)
@@ -612,10 +612,10 @@ class STXBinaryFile(object):
renderInfoBIN = bitmapBIN + fontsBIN + cursorBIN + drawdataBIN
printBinaryDump(renderInfoBIN)
-
+
return renderInfoBIN
if __name__ == '__main__':
bin = STXBinaryFile('../gui/themes/scummclassic', True, True)
bin.parse()
-
+
diff --git a/tools/update-version.pl b/tools/update-version.pl
index 81aa5c27f9..f8a46b9a7e 100755
--- a/tools/update-version.pl
+++ b/tools/update-version.pl
@@ -49,7 +49,8 @@ my %subs = (
VER_MINOR => $VER_MINOR,
VER_PATCH => $VER_PATCH,
VER_EXTRA => $VER_EXTRA,
- VERSION => $VERSION
+ VERSION => $VERSION,
+ SVN_REVISION=> "",
);
foreach my $file (@subs_files) {